Skip to content

Commit fdfcebb

Browse files
schittirFznamznon
andauthored
[clang][SYCL] Add sycl_external attribute and restrict emitting device code (#140282)
This patch is part of the upstreaming effort for supporting SYCL language front end. It makes the following changes: 1. Adds sycl_external attribute for functions with external linkage, which is intended for use to implement the SYCL_EXTERNAL macro as specified by the SYCL 2020 specification 2. Adds checks to avoid emitting device code when sycl_external and sycl_kernel_entry_point attributes are not enabled 3. Fixes test failures caused by the above changes This patch is missing diagnostics for the following diagnostics listed in the SYCL 2020 specification's section 5.10.1, which will be addressed in a subsequent PR: Functions that are declared using SYCL_EXTERNAL have the following additional restrictions beyond those imposed on other device functions: 1. If the SYCL backend does not support the generic address space then the function cannot use raw pointers as parameter or return types. Explicit pointer classes must be used instead; 2. The function cannot call group::parallel_for_work_item; 3. The function cannot be called from a parallel_for_work_group scope. In addition to that, the subsequent PR will also implement diagnostics for inline functions including virtual functions defined as inline. --------- Co-authored-by: Mariya Podchishchaeva <[email protected]>
1 parent 85043c1 commit fdfcebb

30 files changed

+790
-372
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1632,6 +1632,13 @@ def DeviceKernel : DeclOrTypeAttr {
16321632
}];
16331633
}
16341634

1635+
def SYCLExternal : InheritableAttr {
1636+
let Spellings = [CXX11<"clang", "sycl_external">];
1637+
let Subjects = SubjectList<[Function], ErrorDiag>;
1638+
let LangOpts = [SYCLHost, SYCLDevice];
1639+
let Documentation = [SYCLExternalDocs];
1640+
}
1641+
16351642
def SYCLKernelEntryPoint : InheritableAttr {
16361643
let Spellings = [CXX11<"clang", "sycl_kernel_entry_point">];
16371644
let Args = [

clang/include/clang/Basic/AttrDocs.td

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,47 @@ The SYCL kernel in the previous code sample meets these expectations.
476476
}];
477477
}
478478

479+
def SYCLExternalDocs : Documentation {
480+
let Category = DocCatFunction;
481+
let Heading = "sycl_external";
482+
let Content = [{
483+
The ``sycl_external`` attribute indicates that a function defined in another
484+
translation unit may be called by a device function defined in the current
485+
translation unit or, if defined in the current translation unit, the function
486+
may be called by device functions defined in other translation units.
487+
The attribute is intended for use in the implementation of the ``SYCL_EXTERNAL``
488+
macro as specified in section 5.10.1, "SYCL functions and member functions
489+
linkage", of the SYCL 2020 specification.
490+
491+
The attribute only appertains to functions and only those that meet the
492+
following requirements:
493+
494+
* Has external linkage
495+
* Is not explicitly defined as deleted (the function may be an explicitly
496+
defaulted function that is defined as deleted)
497+
498+
The attribute shall be present on the first declaration of a function and
499+
may optionally be present on subsequent declarations.
500+
501+
When compiling for a SYCL device target that does not support the generic
502+
address space, the function shall not specify a raw pointer or reference type
503+
as the return type or as a parameter type.
504+
See section 5.10, "SYCL offline linking", of the SYCL 2020 specification.
505+
The following examples demonstrate the use of this attribute:
506+
507+
.. code-block:: c++
508+
509+
[[clang::sycl_external]] void Foo(); // Ok.
510+
511+
[[clang::sycl_external]] void Bar() { /* ... */ } // Ok.
512+
513+
[[clang::sycl_external]] extern void Baz(); // Ok.
514+
515+
[[clang::sycl_external]] static void Quux() { /* ... */ } // error: Quux() has internal linkage.
516+
517+
}];
518+
}
519+
479520
def SYCLKernelEntryPointDocs : Documentation {
480521
let Category = DocCatFunction;
481522
let Content = [{

clang/include/clang/Basic/DiagnosticGroups.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ def NonNull : DiagGroup<"nonnull">;
652652
def NonPODVarargs : DiagGroup<"non-pod-varargs">;
653653
def ClassVarargs : DiagGroup<"class-varargs", [NonPODVarargs]>;
654654
def : DiagGroup<"nonportable-cfstrings">;
655+
def NonPortableSYCL : DiagGroup<"nonportable-sycl">;
655656
def NonVirtualDtor : DiagGroup<"non-virtual-dtor">;
656657
def GNUNullPointerArithmetic : DiagGroup<"gnu-null-pointer-arithmetic">;
657658
def NullPointerArithmetic

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12958,6 +12958,17 @@ def err_sycl_special_type_num_init_method : Error<
1295812958
"types with 'sycl_special_class' attribute must have one and only one '__init' "
1295912959
"method defined">;
1296012960

12961+
// SYCL external attribute diagnostics
12962+
def err_sycl_external_invalid_linkage : Error<
12963+
"%0 can only be applied to functions with external linkage">;
12964+
def err_sycl_external_invalid_main : Error<
12965+
"%0 cannot be applied to the 'main' function">;
12966+
def err_sycl_external_invalid_deleted_function : Error<
12967+
"%0 cannot be applied to an explicitly deleted function">;
12968+
def warn_sycl_external_missing_on_first_decl : Warning<
12969+
"%0 attribute does not appear on the first declaration">,
12970+
InGroup<NonPortableSYCL>;
12971+
1296112972
// SYCL kernel entry point diagnostics
1296212973
def err_sycl_entry_point_invalid : Error<
1296312974
"the %0 attribute cannot be applied to a"
@@ -12972,7 +12983,7 @@ def err_sycl_kernel_name_conflict : Error<
1297212983
"the %0 kernel name argument conflicts with a previous declaration">;
1297312984
def warn_sycl_kernel_name_not_a_class_type : Warning<
1297412985
"%0 is not a valid SYCL kernel name type; a non-union class type is required">,
12975-
InGroup<DiagGroup<"nonportable-sycl">>, DefaultError;
12986+
InGroup<NonPortableSYCL>, DefaultError;
1297612987
def warn_sycl_entry_point_redundant_declaration : Warning<
1297712988
"redundant %0 attribute">, InGroup<RedundantAttribute>;
1297812989
def err_sycl_entry_point_after_definition : Error<

clang/include/clang/Sema/SemaSYCL.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class SemaSYCL : public SemaBase {
6464
void handleKernelAttr(Decl *D, const ParsedAttr &AL);
6565
void handleKernelEntryPointAttr(Decl *D, const ParsedAttr &AL);
6666

67+
void CheckSYCLExternalFunctionDecl(FunctionDecl *FD);
6768
void CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD);
6869
StmtResult BuildSYCLKernelCallStmt(FunctionDecl *FD, CompoundStmt *Body);
6970
};

clang/lib/AST/ASTContext.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13127,6 +13127,14 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1312713127
if (D->hasAttr<WeakRefAttr>())
1312813128
return false;
1312913129

13130+
// SYCL device compilation requires that functions defined with the
13131+
// sycl_kernel_entry_point or sycl_external attributes be emitted. All
13132+
// other entities are emitted only if they are used by a function
13133+
// defined with one of those attributes.
13134+
if (LangOpts.SYCLIsDevice)
13135+
return isa<FunctionDecl>(D) && (D->hasAttr<SYCLKernelEntryPointAttr>() ||
13136+
D->hasAttr<SYCLExternalAttr>());
13137+
1313013138
// Aliases and used decls are required.
1313113139
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
1313213140
return true;
@@ -13136,15 +13144,6 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1313613144
if (!FD->doesThisDeclarationHaveABody())
1313713145
return FD->doesDeclarationForceExternallyVisibleDefinition();
1313813146

13139-
// Function definitions with the sycl_kernel_entry_point attribute are
13140-
// required during device compilation so that SYCL kernel caller offload
13141-
// entry points are emitted.
13142-
if (LangOpts.SYCLIsDevice && FD->hasAttr<SYCLKernelEntryPointAttr>())
13143-
return true;
13144-
13145-
// FIXME: Functions declared with SYCL_EXTERNAL are required during
13146-
// device compilation.
13147-
1314813147
// Constructors and destructors are required.
1314913148
if (FD->hasAttr<ConstructorAttr>() || FD->hasAttr<DestructorAttr>())
1315013149
return true;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3115,6 +3115,10 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
31153115
cast<SYCLKernelEntryPointAttr>(NewAttribute)->setInvalidAttr();
31163116
++I;
31173117
continue;
3118+
} else if (isa<SYCLExternalAttr>(NewAttribute)) {
3119+
// SYCLExternalAttr may be added after a definition.
3120+
++I;
3121+
continue;
31183122
}
31193123

31203124
S.Diag(NewAttribute->getLocation(),
@@ -4140,6 +4144,18 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
41404144
diag::note_carries_dependency_missing_first_decl) << 0/*Function*/;
41414145
}
41424146

4147+
// SYCL 2020 section 5.10.1, "SYCL functions and member functions linkage":
4148+
// When a function is declared with SYCL_EXTERNAL, that macro must be
4149+
// used on the first declaration of that function in the translation unit.
4150+
// Redeclarations of the function in the same translation unit may
4151+
// optionally use SYCL_EXTERNAL, but this is not required.
4152+
const SYCLExternalAttr *SEA = New->getAttr<SYCLExternalAttr>();
4153+
if (SEA && !Old->hasAttr<SYCLExternalAttr>()) {
4154+
Diag(SEA->getLocation(), diag::warn_sycl_external_missing_on_first_decl)
4155+
<< SEA;
4156+
Diag(Old->getLocation(), diag::note_previous_declaration);
4157+
}
4158+
41434159
// (C++98 8.3.5p3):
41444160
// All declarations for a function shall agree exactly in both the
41454161
// return type and the parameter-type-list.
@@ -12325,6 +12341,9 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1232512341
if (NewFD->hasAttr<SYCLKernelEntryPointAttr>())
1232612342
SYCL().CheckSYCLEntryPointFunctionDecl(NewFD);
1232712343

12344+
if (NewFD->hasAttr<SYCLExternalAttr>())
12345+
SYCL().CheckSYCLExternalFunctionDecl(NewFD);
12346+
1232812347
// Semantic checking for this function declaration (in isolation).
1232912348

1233012349
if (getLangOpts().CPlusPlus) {
@@ -12513,6 +12532,13 @@ void Sema::CheckMain(FunctionDecl *FD, const DeclSpec &DS) {
1251312532
return;
1251412533
}
1251512534

12535+
if (FD->hasAttr<SYCLExternalAttr>()) {
12536+
Diag(FD->getLocation(), diag::err_sycl_external_invalid_main)
12537+
<< FD->getAttr<SYCLExternalAttr>();
12538+
FD->setInvalidDecl();
12539+
return;
12540+
}
12541+
1251612542
// Functions named main in hlsl are default entries, but don't have specific
1251712543
// signatures they are required to conform to.
1251812544
if (getLangOpts().HLSL)
@@ -16351,6 +16377,14 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, bool IsInstantiation,
1635116377
}
1635216378
}
1635316379

16380+
if (FD && !FD->isInvalidDecl() && FD->hasAttr<SYCLExternalAttr>()) {
16381+
SYCLExternalAttr *SEAttr = FD->getAttr<SYCLExternalAttr>();
16382+
if (FD->isDeletedAsWritten())
16383+
Diag(SEAttr->getLocation(),
16384+
diag::err_sycl_external_invalid_deleted_function)
16385+
<< SEAttr;
16386+
}
16387+
1635416388
{
1635516389
// Do not call PopExpressionEvaluationContext() if it is a lambda because
1635616390
// 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
@@ -7061,6 +7061,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
70617061
case ParsedAttr::AT_EnumExtensibility:
70627062
handleEnumExtensibilityAttr(S, D, AL);
70637063
break;
7064+
case ParsedAttr::AT_SYCLExternal:
7065+
handleSimpleAttribute<SYCLExternalAttr>(S, D, AL);
7066+
break;
70647067
case ParsedAttr::AT_SYCLKernelEntryPoint:
70657068
S.SYCL().handleKernelEntryPointAttr(D, AL);
70667069
break;

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,23 @@ static bool CheckSYCLKernelName(Sema &S, SourceLocation Loc,
250250
return false;
251251
}
252252

253+
void SemaSYCL::CheckSYCLExternalFunctionDecl(FunctionDecl *FD) {
254+
const auto *SEAttr = FD->getAttr<SYCLExternalAttr>();
255+
assert(SEAttr && "Missing sycl_external attribute");
256+
if (!FD->isInvalidDecl() && !FD->isTemplated()) {
257+
if (!FD->isExternallyVisible())
258+
if (!FD->isFunctionTemplateSpecialization() ||
259+
FD->getTemplateSpecializationInfo()->isExplicitSpecialization())
260+
Diag(SEAttr->getLocation(), diag::err_sycl_external_invalid_linkage)
261+
<< SEAttr;
262+
}
263+
if (FD->isDeletedAsWritten()) {
264+
Diag(SEAttr->getLocation(),
265+
diag::err_sycl_external_invalid_deleted_function)
266+
<< SEAttr;
267+
}
268+
}
269+
253270
void SemaSYCL::CheckSYCLEntryPointFunctionDecl(FunctionDecl *FD) {
254271
// Ensure that all attributes present on the declaration are consistent
255272
// and warn about any redundant ones.
Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,36 @@
1-
// RUN: %clang_cc1 -O1 -triple spirv64 -fsycl-is-device %s -emit-llvm -o - | FileCheck %s
1+
// RUN: %clang_cc1 -O1 -triple spirv64 -fsycl-is-device -x c++ %s -emit-llvm -o - | FileCheck %s
22
// RUN: %clang_cc1 -O1 -triple spirv64 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s
33
// RUN: %clang_cc1 -O1 -triple spirv32 -cl-std=CL3.0 -x cl %s -emit-llvm -o - | FileCheck %s
44

5-
// CHECK: spir_func noundef ptr @test_cast_to_private(
6-
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
5+
#ifdef __SYCL_DEVICE_ONLY__
6+
#define SYCL_EXTERNAL [[clang::sycl_external]]
7+
#else
8+
#define SYCL_EXTERNAL
9+
#endif
10+
11+
// CHECK: spir_func noundef ptr @{{.*}}test_cast_to_private{{.*}}(ptr addrspace(4) noundef readnone [[P:%.*]]
712
// CHECK-NEXT: [[ENTRY:.*:]]
813
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr @llvm.spv.generic.cast.to.ptr.explicit.p0(ptr addrspace(4) %p)
914
// CHECK-NEXT: ret ptr [[SPV_CAST]]
1015
//
11-
__attribute__((opencl_private)) int* test_cast_to_private(int* p) {
16+
SYCL_EXTERNAL __attribute__((opencl_private)) int* test_cast_to_private(int* p) {
1217
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 7);
1318
}
1419

15-
// CHECK: spir_func noundef ptr addrspace(1) @test_cast_to_global(
16-
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
20+
// CHECK: spir_func noundef ptr addrspace(1) @{{.*}}test_cast_to_global{{.*}}(ptr addrspace(4) noundef readnone [[P:%.*]]
1721
// CHECK-NEXT: [[ENTRY:.*:]]
1822
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(1) @llvm.spv.generic.cast.to.ptr.explicit.p1(ptr addrspace(4) %p)
1923
// CHECK-NEXT: ret ptr addrspace(1) [[SPV_CAST]]
2024
//
21-
__attribute__((opencl_global)) int* test_cast_to_global(int* p) {
25+
SYCL_EXTERNAL __attribute__((opencl_global)) int* test_cast_to_global(int* p) {
2226
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 5);
2327
}
2428

25-
// CHECK: spir_func noundef ptr addrspace(3) @test_cast_to_local(
26-
// CHECK-SAME: ptr addrspace(4) noundef readnone [[P:%.*]]
29+
// CHECK: spir_func noundef ptr addrspace(3) @{{.*}}test_cast_to_local{{.*}}(ptr addrspace(4) noundef readnone [[P:%.*]]
2730
// CHECK-NEXT: [[ENTRY:.*:]]
2831
// CHECK-NEXT: [[SPV_CAST:%.*]] = tail call noundef ptr addrspace(3) @llvm.spv.generic.cast.to.ptr.explicit.p3(ptr addrspace(4) %p)
2932
// CHECK-NEXT: ret ptr addrspace(3) [[SPV_CAST]]
3033
//
31-
__attribute__((opencl_local)) int* test_cast_to_local(int* p) {
34+
SYCL_EXTERNAL __attribute__((opencl_local)) int* test_cast_to_local(int* p) {
3235
return __builtin_spirv_generic_cast_to_ptr_explicit(p, 4);
3336
}

0 commit comments

Comments
 (0)