diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index fe8175fb249b4..b4014a122155c 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2141,6 +2141,22 @@ def SYCLAddIRAnnotationsMember : InheritableAttr { let Documentation = [SYCLAddIRAnnotationsMemberDocs]; } +def SYCLRegisteredKernels : InheritableAttr { + let Spellings = [CXX11<"__sycl_detail__", "__registered_kernels__">]; + let Args = [VariadicExprArgument<"Args">]; + let LangOpts = [SYCLIsDevice, SilentlyIgnoreSYCLIsHost]; + let Subjects = SubjectList<[Empty], ErrorDiag, "Translation Unit Scope">; + let AdditionalMembers = SYCLAddIRAttrCommonMembers.MemberCode; + let Documentation = [SYCLAddIRAnnotationsMemberDocs]; +} + +def SYCLRegisteredKernelName : InheritableAttr { + let Spellings = []; + let Subjects = SubjectList<[Function]>; + let Args = [StringArgument<"RegName">]; + let Documentation = [InternalOnly]; +} + def C11NoReturn : InheritableAttr { let Spellings = [CustomKeyword<"_Noreturn">]; let Subjects = SubjectList<[Function], ErrorDiag>; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 56226fa5bf4c7..0460fd24a013a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12514,6 +12514,20 @@ def err_sycl_special_type_num_init_method : Error< def warn_launch_bounds_is_cuda_specific : Warning< "%0 attribute ignored, only applicable when targeting Nvidia devices">, InGroup; +def err_registered_kernels_num_of_args : Error< + "'__registered_kernels__' attribute must have at least one argument">; +def err_registered_kernels_init_list : Error< + "argument to the '__registered_kernels__' attribute must be an " + "initializer list expression">; +def err_registered_kernels_init_list_pair_values : Error< + "each initializer list argument to the '__registered_kernels__' attribute " + "must contain a pair of values">; +def err_registered_kernels_resolve_function : Error< + "unable to resolve free function kernel '%0'">; +def err_registered_kernels_name_already_registered : Error< + "free function kernel has already been registered with '%0'; cannot register with '%1'">; +def err_not_sycl_free_function : Error< + "attempting to register a function that is not a SYCL free function as '%0'">; def warn_cuda_maxclusterrank_sm_90 : Warning< "'maxclusterrank' requires sm_90 or higher, CUDA arch provided: %0, ignoring " diff --git a/clang/include/clang/Sema/SemaSYCL.h b/clang/include/clang/Sema/SemaSYCL.h index 63880d7438522..46333033a228e 100644 --- a/clang/include/clang/Sema/SemaSYCL.h +++ b/clang/include/clang/Sema/SemaSYCL.h @@ -252,8 +252,9 @@ class SemaSYCL : public SemaBase { // We need to store the list of the sycl_kernel functions and their associated // generated OpenCL Kernels so we can go back and re-name these after the // fact. - llvm::SmallVector> - SyclKernelsToOpenCLKernels; + using KernelFDPairs = + llvm::SmallVector>; + KernelFDPairs SyclKernelsToOpenCLKernels; // Used to suppress diagnostics during kernel construction, since these were // already emitted earlier. Diagnosing during Kernel emissions also skips the @@ -296,11 +297,15 @@ class SemaSYCL : public SemaBase { llvm::DenseSet Visited, ValueDecl *DeclToCheck); + const KernelFDPairs &getKernelFDPairs() { return SyclKernelsToOpenCLKernels; } + void addSyclOpenCLKernel(const FunctionDecl *SyclKernel, FunctionDecl *OpenCLKernel) { SyclKernelsToOpenCLKernels.emplace_back(SyclKernel, OpenCLKernel); } + void constructFreeFunctionKernel(FunctionDecl *FD, StringRef NameStr = ""); + void addSyclDeviceDecl(Decl *d) { SyclDeviceDecls.insert(d); } llvm::SetVector &syclDeviceDecls() { return SyclDeviceDecls; } @@ -480,6 +485,7 @@ class SemaSYCL : public SemaBase { void handleSYCLIntelMaxWorkGroupsPerMultiprocessor(Decl *D, const ParsedAttr &AL); void handleSYCLScopeAttr(Decl *D, const ParsedAttr &AL); + void handleSYCLRegisteredKernels(Decl *D, const ParsedAttr &AL); void checkSYCLAddIRAttributesFunctionAttrConflicts(Decl *D); @@ -655,6 +661,10 @@ class SemaSYCL : public SemaBase { void addIntelReqdSubGroupSizeAttr(Decl *D, const AttributeCommonInfo &CI, Expr *E); void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL); + + // Used to check whether the function represented by FD is a SYCL + // free function kernel or not. + bool isFreeFunction(const FunctionDecl *FD); }; } // namespace clang diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 40a6b1b8b3395..1dc61da430e3f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -641,6 +641,12 @@ void CodeGenFunction::EmitKernelMetadata(const FunctionDecl *FD, llvm::LLVMContext &Context = getLLVMContext(); + if (getLangOpts().SYCLIsDevice) + if (FD->hasAttr()) + CGM.SYCLAddRegKernelNamePairs( + FD->getAttr()->getRegName(), + FD->getNameAsString()); + if (FD->hasAttr() || FD->hasAttr()) CGM.GenKernelArgMetadata(Fn, FD, this); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 4d18be3ebb55b..7a5cc9e6fb7b6 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1427,6 +1427,19 @@ void CodeGenModule::Release() { AspectEnumValsMD->addOperand( getAspectEnumValueMD(Context, TheModule.getContext(), ECD)); } + + if (!SYCLRegKernelNames.empty()) { + std::vector Nodes; + llvm::LLVMContext &Ctx = TheModule.getContext(); + for (auto MDKernelNames : SYCLRegKernelNames) { + llvm::Metadata *Vals[] = {MDKernelNames.first, MDKernelNames.second}; + Nodes.push_back(llvm::MDTuple::get(Ctx, Vals)); + } + + llvm::NamedMDNode *SYCLRegKernelsMD = + TheModule.getOrInsertNamedMetadata("sycl_registered_kernels"); + SYCLRegKernelsMD->addOperand(llvm::MDNode::get(Ctx, Nodes)); + } } // HLSL related end of code gen work items. diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index f0646394e62af..2bdc62ae0b052 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -456,6 +456,9 @@ class CodeGenModule : public CodeGenTypeCache { /// handled differently than regular annotations so they cannot share map. llvm::DenseMap SYCLAnnotationArgs; + typedef std::pair MetadataPair; + SmallVector SYCLRegKernelNames; + llvm::StringMap CFConstantStringMap; llvm::DenseMap ConstantStringMap; @@ -1483,6 +1486,12 @@ class CodeGenModule : public CodeGenTypeCache { llvm::Constant *EmitSYCLAnnotationArgs( SmallVectorImpl> &Pairs); + void SYCLAddRegKernelNamePairs(StringRef First, StringRef Second) { + SYCLRegKernelNames.push_back( + std::make_pair(llvm::MDString::get(getLLVMContext(), First), + llvm::MDString::get(getLLVMContext(), Second))); + } + /// Add attributes from add_ir_attributes_global_variable on TND to GV. void AddGlobalSYCLIRAttributes(llvm::GlobalVariable *GV, const RecordDecl *RD); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 534d8e4e33a64..dfb8f90461d7f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7424,6 +7424,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_SYCLAddIRAnnotationsMember: S.SYCL().handleSYCLAddIRAnnotationsMemberAttr(D, AL); break; + case ParsedAttr::AT_SYCLRegisteredKernels: + S.SYCL().handleSYCLRegisteredKernels(D, AL); + break; // Swift attributes. case ParsedAttr::AT_SwiftAsyncName: diff --git a/clang/lib/Sema/SemaSYCL.cpp b/clang/lib/Sema/SemaSYCL.cpp index 1853a744ab2fa..0aaee401a75ee 100644 --- a/clang/lib/Sema/SemaSYCL.cpp +++ b/clang/lib/Sema/SemaSYCL.cpp @@ -1148,10 +1148,10 @@ static target getAccessTarget(QualType FieldTy, // FIXME: Free functions must have void return type and be declared at file // scope, outside any namespaces. -static bool isFreeFunction(SemaSYCL &SemaSYCLRef, const FunctionDecl *FD) { +bool SemaSYCL::isFreeFunction(const FunctionDecl *FD) { for (auto *IRAttr : FD->specific_attrs()) { SmallVector, 4> NameValuePairs = - IRAttr->getAttributeNameValuePairs(SemaSYCLRef.getASTContext()); + IRAttr->getAttributeNameValuePairs(getASTContext()); for (const auto &NameValuePair : NameValuePairs) { if (NameValuePair.first == "sycl-nd-range-kernel" || NameValuePair.first == "sycl-single-task-kernel") { @@ -5291,7 +5291,7 @@ void SemaSYCL::SetSYCLKernelNames() { SyclKernelsToOpenCLKernels) { std::string CalculatedName, StableName; StringRef KernelName; - if (isFreeFunction(*this, Pair.first)) { + if (isFreeFunction(Pair.first)) { std::tie(CalculatedName, StableName) = constructFreeFunctionKernelName(*this, Pair.first, *MangleCtx); KernelName = CalculatedName; @@ -5414,24 +5414,66 @@ void SemaSYCL::ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc, } } -void ConstructFreeFunctionKernel(SemaSYCL &SemaSYCLRef, FunctionDecl *FD) { - SyclKernelArgsSizeChecker argsSizeChecker(SemaSYCLRef, FD->getLocation(), +static void addRegisteredKernelName(SemaSYCL &S, StringRef Str, + FunctionDecl *FD, SourceLocation Loc) { + if (!Str.empty()) + FD->addAttr(SYCLRegisteredKernelNameAttr::CreateImplicit(S.getASTContext(), + Str, Loc)); +} + +static bool checkAndAddRegisteredKernelName(SemaSYCL &S, FunctionDecl *FD, + StringRef Str) { + using KernelPair = std::pair; + for (const KernelPair &Pair : S.getKernelFDPairs()) { + if (Pair.first == FD) { + // If the current list of free function entries already contains this + // free function, apply the name Str as an attribute. But if it already + // has an attribute name, issue a diagnostic instead. + if (!Str.empty()) { + if (!Pair.second->hasAttr()) + addRegisteredKernelName(S, Str, Pair.second, FD->getLocation()); + else + S.Diag(FD->getLocation(), + diag::err_registered_kernels_name_already_registered) + << Pair.second->getAttr() + ->getRegName() + << Str; + } + // An empty name string implies a regular free kernel construction + // call, so simply return. + return false; + } + } + return true; +} + +void SemaSYCL::constructFreeFunctionKernel(FunctionDecl *FD, + StringRef NameStr) { + if (!checkAndAddRegisteredKernelName(*this, FD, NameStr)) + return; + + SyclKernelArgsSizeChecker argsSizeChecker(*this, FD->getLocation(), false /*IsSIMDKernel*/); - SyclKernelDeclCreator kernel_decl(SemaSYCLRef, FD->getLocation(), - FD->isInlined(), false /*IsSIMDKernel */, - FD); + SyclKernelDeclCreator kernel_decl(*this, FD->getLocation(), FD->isInlined(), + false /*IsSIMDKernel */, FD); - FreeFunctionKernelBodyCreator kernel_body(SemaSYCLRef, kernel_decl, FD); + FreeFunctionKernelBodyCreator kernel_body(*this, kernel_decl, FD); - SyclKernelIntHeaderCreator int_header( - SemaSYCLRef, SemaSYCLRef.getSyclIntegrationHeader(), FD->getType(), FD); + SyclKernelIntHeaderCreator int_header(*this, getSyclIntegrationHeader(), + FD->getType(), FD); - SyclKernelIntFooterCreator int_footer(SemaSYCLRef, - SemaSYCLRef.getSyclIntegrationFooter()); - KernelObjVisitor Visitor{SemaSYCLRef}; + SyclKernelIntFooterCreator int_footer(*this, getSyclIntegrationFooter()); + KernelObjVisitor Visitor{*this}; Visitor.VisitFunctionParameters(FD, argsSizeChecker, kernel_decl, kernel_body, int_header, int_footer); + + assert(getKernelFDPairs().back().first == FD && + "OpenCL Kernel not found for free function entry"); + // Register the kernel name with the OpenCL kernel generated for the + // free function. + addRegisteredKernelName(*this, NameStr, getKernelFDPairs().back().second, + FD->getLocation()); } // Figure out the sub-group for the this function. First we check the @@ -5717,7 +5759,7 @@ void SemaSYCL::MarkDevices() { } void SemaSYCL::ProcessFreeFunction(FunctionDecl *FD) { - if (isFreeFunction(*this, FD)) { + if (isFreeFunction(FD)) { SyclKernelDecompMarker DecompMarker(*this); SyclKernelFieldChecker FieldChecker(*this); SyclKernelUnionChecker UnionChecker(*this); @@ -5736,7 +5778,7 @@ void SemaSYCL::ProcessFreeFunction(FunctionDecl *FD) { if (!FieldChecker.isValid() || !UnionChecker.isValid()) return; - ConstructFreeFunctionKernel(*this, FD); + constructFreeFunctionKernel(FD); } } @@ -6621,7 +6663,7 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) { unsigned ShimCounter = 1; int FreeFunctionCount = 0; for (const KernelDesc &K : KernelDescs) { - if (!isFreeFunction(S, K.SyclKernel)) + if (!S.isFreeFunction(K.SyclKernel)) continue; ++FreeFunctionCount; // Generate forward declaration for free function. @@ -6739,7 +6781,7 @@ void SYCLIntegrationHeader::emit(raw_ostream &O) { } ShimCounter = 1; for (const KernelDesc &K : KernelDescs) { - if (!isFreeFunction(S, K.SyclKernel)) + if (!S.isFreeFunction(K.SyclKernel)) continue; O << "\n// Definition of kernel_id of " << K.Name << "\n"; diff --git a/clang/lib/Sema/SemaSYCLDeclAttr.cpp b/clang/lib/Sema/SemaSYCLDeclAttr.cpp index c2268d9f35b14..491ecc04115a6 100644 --- a/clang/lib/Sema/SemaSYCLDeclAttr.cpp +++ b/clang/lib/Sema/SemaSYCLDeclAttr.cpp @@ -3162,3 +3162,68 @@ void SemaSYCL::checkSYCLAddIRAttributesFunctionAttrConflicts(Decl *D) { Diag(Attr->getLoc(), diag::warn_sycl_old_and_new_kernel_attributes) << Attr; } + +void SemaSYCL::handleSYCLRegisteredKernels(Decl *D, const ParsedAttr &A) { + // Check for SYCL device compilation context. + if (!getLangOpts().SYCLIsDevice) + return; + + unsigned NumArgs = A.getNumArgs(); + // When declared, we expect at least one item in the list. + if (NumArgs == 0) { + Diag(A.getLoc(), diag::err_registered_kernels_num_of_args); + return; + } + + // Traverse through the items in the list. + for (unsigned I = 0; I < NumArgs; I++) { + assert(A.isArgExpr(I) && "Expected expression argument"); + // Each item in the list must be an initializer list expression. + Expr *ArgExpr = A.getArgAsExpr(I); + if (!isa(ArgExpr)) { + Diag(ArgExpr->getExprLoc(), diag::err_registered_kernels_init_list); + return; + } + + auto *ArgListE = cast(ArgExpr); + unsigned NumInits = ArgListE->getNumInits(); + // Each init-list expression must have a pair of values. + if (NumInits != 2) { + Diag(ArgExpr->getExprLoc(), + diag::err_registered_kernels_init_list_pair_values); + return; + } + + // The first value of the pair must be a string. + Expr *FirstExpr = ArgListE->getInit(0); + StringRef CurStr; + SourceLocation Loc = FirstExpr->getExprLoc(); + if (!SemaRef.checkStringLiteralArgumentAttr(A, FirstExpr, CurStr, &Loc)) + return; + + // Resolve the FunctionDecl from the second value of the pair. + Expr *SecondE = ArgListE->getInit(1); + FunctionDecl *FD = nullptr; + if (auto *ULE = dyn_cast(SecondE)) { + FD = SemaRef.ResolveSingleFunctionTemplateSpecialization(ULE, true); + Loc = ULE->getExprLoc(); + } else { + SecondE = SecondE->IgnoreParenCasts(); + if (auto *DRE = dyn_cast(SecondE)) + FD = dyn_cast(DRE->getDecl()); + Loc = SecondE->getExprLoc(); + } + // Issue a diagnostic if we are unable to resolve the FunctionDecl. + if (!FD) { + Diag(Loc, diag::err_registered_kernels_resolve_function) << CurStr; + return; + } + // Issue a diagnostic is the FunctionDecl is not a SYCL free function. + if (!isFreeFunction(FD)) { + Diag(FD->getLocation(), diag::err_not_sycl_free_function) << CurStr; + return; + } + // Construct a free function kernel. + constructFreeFunctionKernel(FD, CurStr); + } +} diff --git a/clang/test/CodeGenSYCL/registered-kernel-names.cpp b/clang/test/CodeGenSYCL/registered-kernel-names.cpp new file mode 100644 index 0000000000000..c8746e34b1e11 --- /dev/null +++ b/clang/test/CodeGenSYCL/registered-kernel-names.cpp @@ -0,0 +1,129 @@ +// RUN: %clang_cc1 -fsycl-is-device -emit-llvm -triple spir64 -o - %s | FileCheck %s + +// This test checks if the sycl_registered_kernels named metadata and +// associated entries are generated for registered kernel names. + +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void foo() { +} + +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void bar(); + +[[__sycl_detail__::add_ir_attributes_function("sycl-nd-range-kernel", 0)]] +void ff_4() { +} + +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void iota(int, int *) { +} + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void tempfoo(T pt); + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void tempfoo2(T pt) { + T t; +} + +template void tempfoo2(int); + +template <> +void tempfoo2(float f); + +template <> +void tempfoo2(short) { } + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void tempfoo3() { + (void)N; +} + +namespace N { +[[__sycl_detail__::__registered_kernels__( + {"foo", foo}, + {"iota", (((void(*)(int, int *))(void *)(((iota)))))}, + {"decl temp", tempfoo}, + {"inst temp", tempfoo2}, + {"decl spec", tempfoo2}, + {"def spec", tempfoo2}, + {"foo3", ff_4}, + {"nontype", tempfoo3<5>}, + {"decl non-temp", bar} +)]]; +} + +// Check that the functions registered in the __registered_kernels__ list +// are defined or declared as appropriate, that the kernels are generated +// for these functions and are called from the generated kernels. + +// Check generation of the SYCL kernel for foo, the call to foo, and definition +// of foo. +// CHECK: define {{.*}} void @_Z17__sycl_kernel_foov() +// CHECK: call {{.*}} void @[[FOO:[_A-Za-z0-9]+]]() +// CHECK: define {{.*}} void @[[FOO:[_A-Za-z0-9]+]]() + +// Check generation of the SYCL kernel for ff_4, the call to ff_4, and the +// definition of ff_4. +// CHECK: define {{.*}} void @_Z18__sycl_kernel_ff_4v() +// CHECK: call {{.*}} void @[[FF_4:[_A-Za-z0-9]+]]() +// CHECK: define {{.*}} void @[[FF_4]]() + +// Check generation of the SYCL kernel for iota, the call to iota, and the +// definition of iota. +// CHECK: define {{.*}} void @_Z18__sycl_kernel_iotaiPi(i32 {{.*}} %__arg_, ptr addrspace(1) {{.*}} %__arg_1) +// CHECK: call {{.*}} void @[[IOTA:[_A-Za-z0-9]+]](i32 {{.*}} %0, ptr addrspace(4) {{.*}} %2) +// CHECK: define {{.*}} void @[[IOTA]](i32 {{.*}} %0, ptr addrspace(4) {{.*}} %1) + +// Check generation of the SYCL kernel for tempfoo2, the call to +// tempfoo2 and the definition of tempfoo2 explicitly instantiated +// with int. +// CHECK: define {{.*}} void @_Z22__sycl_kernel_tempfoo2IiEvT_(i32 {{.*}} %__arg_pt) +// CHECK: call {{.*}} void @[[TEMPFOO2INT:[_A-Za-z0-9]+]](i32 {{.*}} %0) +// CHECK: define {{.*}} void @[[TEMPFOO2INT]](i32 {{.*}} %pt) + +// Check generation of the SYCL kernel for tempfoo2, the call to +// tempfoo2 and the definition of tempfoo2 specialized with short. +// CHECK: define {{.*}} void @_Z22__sycl_kernel_tempfoo2IsEvT_(i16 {{.*}} %__arg_) +// CHECK: call {{.*}} void @[[TEMPFOO2SHORT:[_A-Za-z0-9]+]](i16 {{.*}} %0) +// CHECK: define {{.*}} void @[[TEMPFOO2SHORT]](i16 {{.*}} %0) + +// Check generation of the SYCL kernel for tempfoo, the call to +// tempfoo, and its declaration. +// CHECK: define {{.*}} void @_Z21__sycl_kernel_tempfooIiEvT_(i32 {{.*}} %__arg_pt) +// CHECK: call {{.*}} void @[[TEMPFOOINT:[_A-Za-z0-9]+]](i32 {{.*}} %0) +// CHECK: declare {{.*}} void @[[TEMPFOOINT]](i32 {{.*}}) + +// Check generation of the SYCL kernel for tempfoo2, the call to +// tempfoo2, and its declaration. +// CHECK: define {{.*}} void @_Z22__sycl_kernel_tempfoo2IfEvT_(float {{.*}} %__arg_f) +// CHECK: call {{.*}} void @[[TEMPFOO2FLOAT:[_A-Za-z0-9]+]](float {{.*}} %0) +// CHECK: declare {{.*}} void @[[TEMPFOO2FLOAT]](float {{.*}}) + +// Check generation of the SYCL kernel for tempfoo3<5>, the call to +// tempfoo3<5>, and its definition. +// CHECK: define {{.*}} void @_Z22__sycl_kernel_tempfoo3ILi5EEvv() +// CHECK: call {{.*}} void @[[TEMPFOO35:[_A-Za-z0-9]+]]() +// CHECK: define {{.*}} void @[[TEMPFOO35]]() + +// Check generation of the SYCL kernel for bar, the call to +// bar, and its declaration. +// CHECK: define {{.*}} void @_Z17__sycl_kernel_barv() +// CHECK: call {{.*}} void @[[BAR:[_A-Za-z0-9]+]]() +// CHECK: declare {{.*}} void @[[BAR]]() + +// Check for the presence of sycl_registered_kernels named metadata. +// CHECK: !sycl_registered_kernels = !{![[LIST:[0-9]+]]} +// CHECK: ![[LIST]] = !{![[ENT1:[0-9]+]], ![[ENT2:[0-9]+]], ![[ENT3:[0-9]+]], ![[ENT4:[0-9]+]], ![[ENT5:[0-9]+]], ![[ENT6:[0-9]+]], ![[ENT7:[0-9]+]], ![[ENT8:[0-9]+]], ![[ENT9:[0-9]+]]} +// CHECK: ![[ENT1]] = !{!"foo", !"{{.*}}sycl_kernel{{.*}}foo{{.*}}"} +// CHECK: ![[ENT2]] = !{!"foo3", !"{{.*}}sycl_kernel{{.*}}ff_4{{.*}}"} +// CHECK: ![[ENT3]] = !{!"iota", !"{{.*}}sycl_kernel{{.*}}iota{{.*}}"} +// CHECK: ![[ENT4]] = !{!"inst temp", !"{{.*}}sycl_kernel{{.*}}tempfoo2{{.*}}"} +// CHECK: ![[ENT5]] = !{!"def spec", !"{{.*}}sycl_kernel{{.*}}tempfoo2{{.*}}"} +// CHECK: ![[ENT6]] = !{!"decl temp", !"{{.*}}sycl_kernel{{.*}}tempfoo{{.*}}"} +// CHECK: ![[ENT7]] = !{!"decl spec", !"{{.*}}sycl_kernel{{.*}}tempfoo2{{.*}}"} +// CHECK: ![[ENT8]] = !{!"nontype", !"{{.*}}sycl_kernel{{.*}}tempfoo3{{.*}}"} +// CHECK: ![[ENT9]] = !{!"decl non-temp", !"{{.*}}sycl_kernel{{.*}}bar{{.*}}"} diff --git a/clang/test/SemaSYCL/registered-kernel-names.cpp b/clang/test/SemaSYCL/registered-kernel-names.cpp new file mode 100644 index 0000000000000..65a55d4379981 --- /dev/null +++ b/clang/test/SemaSYCL/registered-kernel-names.cpp @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s + +// The test checks issuing diagnostics for registered kernel names. + +// expected-error@+1 {{attempting to register a function that is not a SYCL free function as 'kernelnamefoo'}} +void foo(); + +constexpr const char *str = "foo"; + +// expected-error@+1 {{'__registered_kernels__' attribute must have at least one argument}} +[[__sycl_detail__::__registered_kernels__( +)]]; + +// expected-error@+2 {{argument to the '__registered_kernels__' attribute must be an initializer list expression}} +[[__sycl_detail__::__registered_kernels__( + 1 +)]]; + +// expected-error@+2 {{each initializer list argument to the '__registered_kernels__' attribute must contain a pair of values}} +[[__sycl_detail__::__registered_kernels__( + {} +)]]; + +// expected-error@+2 {{each initializer list argument to the '__registered_kernels__' attribute must contain a pair of values}} +[[__sycl_detail__::__registered_kernels__( + { "foo" } +)]]; + +// expected-error@+2 {{unable to resolve free function kernel 'foo'}} +[[__sycl_detail__::__registered_kernels__( + { "foo", "foo" } +)]]; + +// expected-error@+2 {{'__registered_kernels__' attribute requires a string}} +[[__sycl_detail__::__registered_kernels__( + { str, 1 } +)]]; + +// expected-error@+2 {{each initializer list argument to the '__registered_kernels__' attribute must contain a pair of values}} +[[__sycl_detail__::__registered_kernels__( + { "foo", 1, foo } +)]]; + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void func1(); + +namespace N { +[[__sycl_detail__::__registered_kernels__( + {"kernelnamefoo", foo} +)]]; +} + +// expected-error@+3 {{unable to resolve free function kernel 'func'}} +namespace { +[[__sycl_detail__::__registered_kernels__( + {"func", func1} +)]]; +} + +// expected-error@+2 {{free function kernel has already been registered with 'reg1'; cannot register with 'reg2'}} +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void good1() { +} + +[[__sycl_detail__::__registered_kernels__( + {"reg1", good1}, + {"reg2", good1} +)]]; + + +struct S1 { +// expected-error@+1 {{'int &' cannot be used as the type of a kernel parameter}} + int &ri; +}; + +template +[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 0)]] +void func_with_S1_param(T s) { +} + +[[__sycl_detail__::__registered_kernels__( + {"ref field", func_with_S1_param} +)]];