Skip to content

Commit 4f5358e

Browse files
committed
[SYCL] The sycl_kernel_entry_point attribute.
The `sycl_kernel_entry_point` attribute is used to declare a function that defines a pattern for an offload kernel to be emitted. The attribute requires a single type argument that specifies a class type that meets the requirements for a SYCL kernel name as described in section 5.2, "Naming of kernels", of the SYCL 2020 specification. A unique kernel name type is required for each function declared with the attribute. The attribute may not first appear on a declaration that follows a definition of the function. The function is required to have a `void` return type. The function's parameters define the parameters to the offload kernel. The function must not be a non-static member function, be deleted or defaulted, be declared with the `constexpr` or `consteval` specifiers, be declared with the `[[noreturn]]` attribute, be a coroutine, or accept variadic arguments. Properties of the offload kernel are collected when a function declared with the `sycl_kernel_entry_point` attribute is parsed or instantiated. These properties, such as the kernel name type, are stored in the AST context where they are (or will be) used for diagnostic purposes and to facilitate reflection to a SYCL run-time library. These properties are not serialized with the AST but are recreated upon deserialization. The `sycl_kernel_entry_point` attribute is intended to replace the existing `sycl_kernel` attribute which is intended to be deprecated in a future change and removed following an appropriate deprecation period. The new attribute differs in that it is enabled for both SYCL host and device compilation, may be used with non-template functions, explicitly indicates the type used as the kernel name type, and will impact AST generation. This change adds the basic infrastructure for the new attribute. Future changes will alter the AST produced for a function defined with the attribute and drive generation of the corresponding offload kernel.
1 parent 4b4ea6d commit 4f5358e

17 files changed

+1036
-3
lines changed

clang/include/clang/AST/ASTContext.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "clang/AST/ExternalASTSource.h"
2424
#include "clang/AST/PrettyPrinter.h"
2525
#include "clang/AST/RawCommentList.h"
26+
#include "clang/AST/SYCLKernelInfo.h"
2627
#include "clang/AST/TemplateName.h"
2728
#include "clang/Basic/LLVM.h"
2829
#include "clang/Basic/PartialDiagnostic.h"
@@ -1222,6 +1223,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
12221223
/// in device compilation.
12231224
llvm::DenseSet<const FunctionDecl *> CUDAImplicitHostDeviceFunUsedByDevice;
12241225

1226+
/// Map of SYCL kernels indexed by the unique type used to name the kernel.
1227+
/// Entries are not serialized but are recreated on deserialization of a
1228+
/// sycl_kernel_entry_point attributed function declaration.
1229+
llvm::DenseMap<CanQualType, SYCLKernelInfo> SYCLKernels;
1230+
12251231
/// For capturing lambdas with an explicit object parameter whose type is
12261232
/// derived from the lambda type, we need to perform derived-to-base
12271233
/// conversion so we can access the captures; the cast paths for that
@@ -3301,6 +3307,22 @@ class ASTContext : public RefCountedBase<ASTContext> {
33013307
void getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
33023308
GlobalDecl GD) const;
33033309

3310+
/// Generates and stores SYCL kernel metadata for the provided
3311+
/// SYCL kernel entry point function. The provided function must have
3312+
/// an attached sycl_kernel_entry_point attribute that specifies a unique
3313+
/// type for the name of a SYCL kernel.
3314+
void registerSYCLEntryPointFunction(FunctionDecl *FD);
3315+
3316+
/// Given a type used as a SYCL kernel name, returns a reference to the
3317+
/// metadata generated from the corresponding SYCL kernel entry point.
3318+
/// Aborts if the provided type is not a registered SYCL kernel name.
3319+
const SYCLKernelInfo &getSYCLKernelInfo(QualType T) const;
3320+
3321+
/// Returns a pointer to the metadata generated from the corresponding
3322+
/// SYCLkernel entry point if the provided type corresponds to a registered
3323+
/// SYCL kernel name. Returns a null pointer otherwise.
3324+
const SYCLKernelInfo *findSYCLKernelInfo(QualType T) const;
3325+
33043326
//===--------------------------------------------------------------------===//
33053327
// Statistics
33063328
//===--------------------------------------------------------------------===//
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===--- SYCLKernelInfo.h --- Information about SYCL kernels --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
/// \file
9+
/// This file declares types used to describe SYCL kernels.
10+
///
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_AST_SYCLKERNELINFO_H
14+
#define LLVM_CLANG_AST_SYCLKERNELINFO_H
15+
16+
#include <string>
17+
#include "clang/AST/Decl.h"
18+
#include "clang/AST/Type.h"
19+
20+
namespace clang {
21+
22+
class SYCLKernelInfo {
23+
public:
24+
SYCLKernelInfo(
25+
CanQualType KernelNameType,
26+
const FunctionDecl *KernelEntryPointDecl)
27+
:
28+
KernelNameType(KernelNameType),
29+
KernelEntryPointDecl(KernelEntryPointDecl)
30+
{}
31+
32+
CanQualType GetKernelNameType() const {
33+
return KernelNameType;
34+
}
35+
36+
const FunctionDecl* GetKernelEntryPointDecl() const {
37+
return KernelEntryPointDecl;
38+
}
39+
40+
private:
41+
CanQualType KernelNameType;
42+
const FunctionDecl *KernelEntryPointDecl;
43+
};
44+
45+
} // namespace clang
46+
47+
#endif // LLVM_CLANG_AST_SYCLKERNELINFO_H

clang/include/clang/Basic/Attr.td

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,8 @@ def MicrosoftExt : LangOpt<"MicrosoftExt">;
407407
def Borland : LangOpt<"Borland">;
408408
def CUDA : LangOpt<"CUDA">;
409409
def HIP : LangOpt<"HIP">;
410-
def SYCL : LangOpt<"SYCLIsDevice">;
410+
def SYCLHost : LangOpt<"SYCLIsHost">;
411+
def SYCLDevice : LangOpt<"SYCLIsDevice">;
411412
def COnly : LangOpt<"", "!LangOpts.CPlusPlus">;
412413
def CPlusPlus : LangOpt<"CPlusPlus">;
413414
def OpenCL : LangOpt<"OpenCL">;
@@ -1489,14 +1490,23 @@ def : MutualExclusions<[CUDAConstant, CUDAShared, HIPManaged]>;
14891490
def SYCLKernel : InheritableAttr {
14901491
let Spellings = [Clang<"sycl_kernel">];
14911492
let Subjects = SubjectList<[FunctionTmpl]>;
1492-
let LangOpts = [SYCL];
1493+
let LangOpts = [SYCLDevice];
14931494
let Documentation = [SYCLKernelDocs];
14941495
}
14951496

1497+
def SYCLKernelEntryPoint : InheritableAttr {
1498+
let Spellings = [Clang<"sycl_kernel_entry_point">];
1499+
let Args = [TypeArgument<"KernelName">];
1500+
let Subjects = SubjectList<[Function], ErrorDiag>;
1501+
let TemplateDependent = 1;
1502+
let LangOpts = [SYCLHost, SYCLDevice];
1503+
let Documentation = [SYCLKernelEntryPointDocs];
1504+
}
1505+
14961506
def SYCLSpecialClass: InheritableAttr {
14971507
let Spellings = [Clang<"sycl_special_class">];
14981508
let Subjects = SubjectList<[CXXRecord]>;
1499-
let LangOpts = [SYCL];
1509+
let LangOpts = [SYCLDevice];
15001510
let Documentation = [SYCLSpecialClassDocs];
15011511
}
15021512

clang/include/clang/Basic/AttrDocs.td

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,64 @@ The SYCL kernel in the previous code sample meets these expectations.
455455
}];
456456
}
457457

458+
def SYCLKernelEntryPointDocs : Documentation {
459+
let Category = DocCatFunction;
460+
let Content = [{
461+
The ``sycl_kernel_entry_point`` attribute specifies that a function definition
462+
defines a pattern for an offload kernel entry point function to be emitted when
463+
the source code is compiled with ``-fsycl`` for a device target. Such functions
464+
serve as the execution entry point for a SYCL run-time library to invoke a SYCL
465+
kernel on a device. The function's parameters define the parameters to the
466+
offload kernel.
467+
468+
The attribute requires a single type argument that specifies a class type that
469+
meets the requirements for a SYCL kernel name as described in section 5.2,
470+
"Naming of kernels", of the SYCL 2020 specification. A unique kernel name type
471+
is required for each function declared with the attribute. The attribute may
472+
not first appear on a declaration that follows a definition of the function.
473+
474+
The attribute appertains only to non-member functions and static member
475+
functions that meet the following requirements:
476+
477+
- Has a ``void`` return type.
478+
- Is not a variadic function.
479+
- Is not a coroutine.
480+
- Is not defined as deleted or as defaulted.
481+
- Is not declared with the ``constexpr`` or ``consteval`` specifiers.
482+
- Is not declared with the ``[[noreturn]]`` attribute.
483+
484+
This attribute is intended for use in the implementation of SYCL run-time
485+
libraries that implement SYCL kernel invocation functions like the
486+
``single_task`` and ``parallel_for`` member functions of the ``sycl::handler``
487+
class specified in section 4.9.4, "Command group ``handler`` class" of the
488+
SYCL 2020 specification. Such use might look something like the following.
489+
490+
.. code-block:: c++
491+
492+
namespace sycl {
493+
class handler {
494+
template<typename KernelNameType, typename KernelType>
495+
[[ clang::sycl_kernel_entry_point(KernelNameType) ]]
496+
static void kernel_entry_point(KernelType kernel) {
497+
kernel();
498+
}
499+
500+
public:
501+
template<typename KernelNameType, typename KernelType>
502+
void single_task(KernelType kernel) {
503+
kernel_entry_point<KernelNameType>(kernel);
504+
}
505+
};
506+
} // namespace sycl
507+
508+
It is not necessary for a SYCL kernel entry point function to be called for
509+
the offload kernel entry point to be emitted. For inline functions and function
510+
templates, any ODR-use will suffice. For other functions, an ODR-use is not
511+
required; the offload kernel entry point will be emitted if the function is
512+
defined.
513+
}];
514+
}
515+
458516
def SYCLSpecialClassDocs : Documentation {
459517
let Category = DocCatStmt;
460518
let Content = [{

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,7 @@ def PoundPragmaMessage : DiagGroup<"#pragma-messages">,
641641
def : DiagGroup<"redundant-decls">;
642642
def RedeclaredClassMember : DiagGroup<"redeclared-class-member">;
643643
def GNURedeclaredEnum : DiagGroup<"gnu-redeclared-enum">;
644+
def RedundantAttribute : DiagGroup<"redundant-attribute">;
644645
def RedundantMove : DiagGroup<"redundant-move">;
645646
def Register : DiagGroup<"register", [DeprecatedRegister]>;
646647
def ReturnTypeCLinkage : DiagGroup<"return-type-c-linkage">;

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12288,6 +12288,31 @@ def err_sycl_special_type_num_init_method : Error<
1228812288
"types with 'sycl_special_class' attribute must have one and only one '__init' "
1228912289
"method defined">;
1229012290

12291+
// SYCL kernel entry point diagnostics
12292+
def err_sycl_entry_point_invalid : Error<
12293+
"'sycl_kernel_entry_point' attribute cannot be applied to a"
12294+
" %select{non-static member|variadic|deleted|defaulted|constexpr|consteval|"
12295+
"noreturn|coroutine}0 function">;
12296+
def err_sycl_entry_point_invalid_redeclaration : Error<
12297+
"'sycl_kernel_entry_point' kernel name argument does not match prior"
12298+
" declaration%diff{: $ vs $|}0,1">;
12299+
def err_sycl_kernel_name_conflict : Error<
12300+
"'sycl_kernel_entry_point' kernel name argument conflicts with a previous"
12301+
" declaration">;
12302+
def warn_sycl_kernel_name_not_a_class_type : Warning<
12303+
"%0 is not a valid SYCL kernel name type; a class type is required">,
12304+
InGroup<DiagGroup<"nonportable-sycl">>, DefaultError;
12305+
def warn_sycl_entry_point_redundant_declaration : Warning<
12306+
"redundant 'sycl_kernel_entry_point' attribute">, InGroup<RedundantAttribute>;
12307+
def err_sycl_entry_point_after_definition : Error<
12308+
"'sycl_kernel_entry_point' attribute cannot be added to a function after the"
12309+
" function is defined">;
12310+
def err_sycl_entry_point_return_type : Error<
12311+
"'sycl_kernel_entry_point' attribute only applies to functions with a"
12312+
" 'void' return type">;
12313+
def err_sycl_entry_point_on_main : Error<
12314+
"'main' cannot be declared with the 'sycl_kernel_entry_point' attribute">;
12315+
1229112316
def warn_cuda_maxclusterrank_sm_90 : Warning<
1229212317
"maxclusterrank requires sm_90 or higher, CUDA arch provided: %0, ignoring "
1229312318
"%1 attribute">, InGroup<IgnoredAttributes>;

clang/include/clang/Sema/SemaSYCL.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ class SemaSYCL : public SemaBase {
6262
ParsedType ParsedTy);
6363

6464
void handleKernelAttr(Decl *D, const ParsedAttr &AL);
65+
void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL);
66+
67+
void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
6568
};
6669

6770
} // namespace clang

clang/lib/AST/ASTContext.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14296,6 +14296,44 @@ void ASTContext::getFunctionFeatureMap(llvm::StringMap<bool> &FeatureMap,
1429614296
}
1429714297
}
1429814298

14299+
static SYCLKernelInfo BuildSYCLKernelInfo(ASTContext &Context,
14300+
CanQualType KernelNameType,
14301+
const FunctionDecl *FD) {
14302+
return { KernelNameType, FD };
14303+
}
14304+
14305+
void ASTContext::registerSYCLEntryPointFunction(FunctionDecl *FD) {
14306+
assert(!FD->isInvalidDecl());
14307+
assert(!FD->isDependentContext());
14308+
14309+
const auto *SKEPAttr = FD->getAttr<SYCLKernelEntryPointAttr>();
14310+
assert(SKEPAttr && "Missing sycl_kernel_entry_point attribute");
14311+
14312+
CanQualType KernelNameType = getCanonicalType(SKEPAttr->getKernelName());
14313+
auto IT = SYCLKernels.find(KernelNameType);
14314+
if (IT != SYCLKernels.end()) {
14315+
if (!declaresSameEntity(FD, IT->second.GetKernelEntryPointDecl()))
14316+
llvm::report_fatal_error("SYCL kernel name conflict");
14317+
} else {
14318+
SYCLKernels.insert_or_assign(
14319+
KernelNameType,
14320+
BuildSYCLKernelInfo(*this, KernelNameType, FD));
14321+
}
14322+
}
14323+
14324+
const SYCLKernelInfo &ASTContext::getSYCLKernelInfo(QualType T) const {
14325+
CanQualType KernelNameType = getCanonicalType(T);
14326+
return SYCLKernels.at(KernelNameType);
14327+
}
14328+
14329+
const SYCLKernelInfo *ASTContext::findSYCLKernelInfo(QualType T) const {
14330+
CanQualType KernelNameType = getCanonicalType(T);
14331+
auto IT = SYCLKernels.find(KernelNameType);
14332+
if (IT != SYCLKernels.end())
14333+
return &IT->second;
14334+
return nullptr;
14335+
}
14336+
1429914337
OMPTraitInfo &ASTContext::getNewOMPTraitInfo() {
1430014338
OMPTraitInfoVector.emplace_back(new OMPTraitInfo());
1430114339
return *OMPTraitInfoVector.back();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#include "clang/Sema/SemaPPC.h"
5555
#include "clang/Sema/SemaRISCV.h"
5656
#include "clang/Sema/SemaSwift.h"
57+
#include "clang/Sema/SemaSYCL.h"
5758
#include "clang/Sema/SemaWasm.h"
5859
#include "clang/Sema/Template.h"
5960
#include "llvm/ADT/STLForwardCompat.h"
@@ -3017,6 +3018,16 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
30173018
// declarations after definitions.
30183019
++I;
30193020
continue;
3021+
} else if (isa<SYCLKernelEntryPointAttr>(NewAttribute)) {
3022+
// Elevate latent uses of the sycl_kernel_entry_point attribute to an
3023+
// error since the definition will have already been created without
3024+
// the semantic effects of the attribute having been applied.
3025+
S.Diag(NewAttribute->getLocation(),
3026+
diag::err_sycl_entry_point_after_definition);
3027+
S.Diag(Def->getLocation(), diag::note_previous_definition);
3028+
New->setInvalidDecl();
3029+
++I;
3030+
continue;
30203031
}
30213032

30223033
S.Diag(NewAttribute->getLocation(),
@@ -12053,6 +12064,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1205312064
if (LangOpts.OpenMP)
1205412065
OpenMP().ActOnFinishedFunctionDefinitionInOpenMPAssumeScope(NewFD);
1205512066

12067+
if (LangOpts.isSYCL() && NewFD->hasAttr<SYCLKernelEntryPointAttr>())
12068+
SYCL().CheckSYCLEntryPointFunctionDecl(NewFD);
12069+
1205612070
// Semantic checking for this function declaration (in isolation).
1205712071

1205812072
if (getLangOpts().CPlusPlus) {
@@ -12285,6 +12299,13 @@ void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) {
1228512299
return;
1228612300
}
1228712301

12302+
if (getLangOpts().isSYCL() && FD->hasAttr<SYCLKernelEntryPointAttr>()) {
12303+
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
12304+
diag::err_sycl_entry_point_on_main);
12305+
FD->setInvalidDecl();
12306+
return;
12307+
}
12308+
1228812309
// Functions named main in hlsl are default entries, but don't have specific
1228912310
// signatures they are required to conform to.
1229012311
if (getLangOpts().HLSL)
@@ -15841,6 +15862,27 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body,
1584115862
CheckCoroutineWrapper(FD);
1584215863
}
1584315864

15865+
// Create SYCL kernel entry point function outline.
15866+
if (FD && !FD->isInvalidDecl() && !FD->isDependentContext() &&
15867+
FD->hasAttr<SYCLKernelEntryPointAttr>()) {
15868+
if (FD->isDeleted()) {
15869+
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
15870+
diag::err_sycl_entry_point_invalid)
15871+
<< /*deleted function*/2;
15872+
FD->setInvalidDecl();
15873+
} else if (FD->isDefaulted()) {
15874+
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
15875+
diag::err_sycl_entry_point_invalid)
15876+
<< /*defaulted function*/3;
15877+
FD->setInvalidDecl();
15878+
} else if (FSI->isCoroutine()) {
15879+
Diag(FD->getAttr<SYCLKernelEntryPointAttr>()->getLocation(),
15880+
diag::err_sycl_entry_point_invalid)
15881+
<< /*coroutine*/7;
15882+
FD->setInvalidDecl();
15883+
}
15884+
}
15885+
1584415886
{
1584515887
// Do not call PopExpressionEvaluationContext() if it is a lambda because
1584615888
// one is already popped when finishing the lambda in BuildLambdaExpr().

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6606,6 +6606,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
66066606
case ParsedAttr::AT_SYCLKernel:
66076607
S.SYCL().handleKernelAttr(D, AL);
66086608
break;
6609+
case ParsedAttr::AT_SYCLKernelEntryPoint:
6610+
S.SYCL().handleKernelEntryPointAttr(D, AL);
6611+
break;
66096612
case ParsedAttr::AT_SYCLSpecialClass:
66106613
handleSimpleAttribute<SYCLSpecialClassAttr>(S, D, AL);
66116614
break;

0 commit comments

Comments
 (0)