Skip to content

Commit 3baec18

Browse files
[SYCL] Allow SYCL_EXTERNAL for class member functions (#1295)
This patch allows to apply sycl_device attribute and therefore SYCL_EXTERNAL macro to class member functions. The same logic was applied to `[[intel::device_indireclty_callable]]`. Moved device code outlining to CodeGen because the old scheme in combination with SYCL_EXTERNAL implementation for methods triggered assertions Signed-off-by: Mariya Podchishchaeva <[email protected]> Co-authored-by: Alexey Sachkov <[email protected]>
1 parent a00416b commit 3baec18

11 files changed

+180
-95
lines changed

clang/include/clang/Basic/AttrDocs.td

+2-2
Original file line numberDiff line numberDiff line change
@@ -2117,8 +2117,8 @@ def SYCLDeviceIndirectlyCallableDocs : Documentation {
21172117
This attribute can only be applied to functions and indicates that the
21182118
function must be treated as a device function and must be emitted even if it has
21192119
no direct uses from other SYCL device functions. However, it cannot be applied
2120-
to functions marked as 'static', functions declared within an anonymous
2121-
namespace or class member functions.
2120+
to functions marked as 'static' and functions declared within an anonymous
2121+
namespace.
21222122

21232123
It also means that function should be available externally and
21242124
cannot be optimized out due to reachability analysis or by any other

clang/include/clang/Basic/DiagnosticSemaKinds.td

+1-2
Original file line numberDiff line numberDiff line change
@@ -10694,8 +10694,7 @@ def warn_boolean_attribute_argument_is_not_valid: Warning<
1069410694
InGroup<AdjustedAttributes>;
1069510695
def err_sycl_attibute_cannot_be_applied_here
1069610696
: Error<"%0 attribute cannot be applied to a "
10697-
"%select{static function or function in an anonymous namespace"
10698-
"|class member function}1">;
10697+
"static function or function in an anonymous namespace">;
1069910698
def warn_sycl_attibute_function_raw_ptr
1070010699
: Warning<"SYCL 1.2.1 specification does not allow %0 attribute applied "
1070110700
"to a function with a raw pointer "

clang/lib/AST/ASTContext.cpp

+14
Original file line numberDiff line numberDiff line change
@@ -10156,6 +10156,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1015610156
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
1015710157
return true;
1015810158

10159+
if (LangOpts.SYCLIsDevice && !D->hasAttr<OpenCLKernelAttr>() &&
10160+
!D->hasAttr<SYCLDeviceAttr>())
10161+
return false;
10162+
1015910163
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
1016010164
// Forward declarations aren't required.
1016110165
if (!FD->doesThisDeclarationHaveABody())
@@ -10178,6 +10182,16 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1017810182
}
1017910183
}
1018010184

10185+
// Methods explcitly marked with 'sycl_device' attribute (via SYCL_EXTERNAL)
10186+
// or `indirectly_callable' attribute must be emitted regardless of number
10187+
// of actual uses
10188+
if (LangOpts.SYCLIsDevice && isa<CXXMethodDecl>(D)) {
10189+
if (auto *A = D->getAttr<SYCLDeviceIndirectlyCallableAttr>())
10190+
return !A->isImplicit();
10191+
if (auto *A = D->getAttr<SYCLDeviceAttr>())
10192+
return !A->isImplicit();
10193+
}
10194+
1018110195
GVALinkage Linkage = GetGVALinkageForFunction(FD);
1018210196

1018310197
// static, static inline, always_inline, and extern inline functions can

clang/lib/CodeGen/CodeGenModule.cpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -2516,11 +2516,6 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
25162516
if (Global->hasAttr<IFuncAttr>())
25172517
return emitIFuncDefinition(GD);
25182518

2519-
if (LangOpts.SYCLIsDevice) {
2520-
if (!Global->hasAttr<SYCLDeviceAttr>())
2521-
return;
2522-
}
2523-
25242519
// If this is a cpu_dispatch multiversion function, emit the resolver.
25252520
if (Global->hasAttr<CPUDispatchAttr>())
25262521
return emitCPUDispatchDefinition(GD);
@@ -2565,6 +2560,11 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
25652560
}
25662561
}
25672562

2563+
if (LangOpts.SYCLIsDevice && MustBeEmitted(Global)) {
2564+
addDeferredDeclToEmit(GD);
2565+
return;
2566+
}
2567+
25682568
// Ignore declarations, they will be emitted on their first use.
25692569
if (const auto *FD = dyn_cast<FunctionDecl>(Global)) {
25702570
// Forward declarations are emitted lazily on first use.

clang/lib/Sema/SemaDeclAttr.cpp

+2-16
Original file line numberDiff line numberDiff line change
@@ -4428,13 +4428,7 @@ static void handleOptimizeNoneAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
44284428
static void handleSYCLDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
44294429
auto *FD = cast<FunctionDecl>(D);
44304430
if (!FD->isExternallyVisible()) {
4431-
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4432-
<< AL << 0 /* static function or anonymous namespace */;
4433-
return;
4434-
}
4435-
if (isa<CXXMethodDecl>(FD)) {
4436-
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4437-
<< AL << 1 /* class member function */;
4431+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here) << AL;
44384432
return;
44394433
}
44404434
if (FD->getReturnType()->isPointerType()) {
@@ -4447,25 +4441,17 @@ static void handleSYCLDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
44474441
<< AL << 1 /* function with a raw pointer parameter type */;
44484442
}
44494443

4450-
S.addSyclDeviceDecl(D);
44514444
handleSimpleAttribute<SYCLDeviceAttr>(S, D, AL);
44524445
}
44534446

44544447
static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D,
44554448
const ParsedAttr &AL) {
44564449
auto *FD = cast<FunctionDecl>(D);
44574450
if (!FD->isExternallyVisible()) {
4458-
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4459-
<< AL << 0 /* static function or anonymous namespace */;
4460-
return;
4461-
}
4462-
if (isa<CXXMethodDecl>(FD)) {
4463-
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4464-
<< AL << 1 /* class member function */;
4451+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here) << AL;
44654452
return;
44664453
}
44674454

4468-
S.addSyclDeviceDecl(D);
44694455
D->addAttr(SYCLDeviceAttr::CreateImplicit(S.Context));
44704456
handleSimpleAttribute<SYCLDeviceIndirectlyCallableAttr>(S, D, AL);
44714457
}

clang/lib/Sema/SemaSYCL.cpp

+1-58
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
228228

229229
CheckSYCLType(Callee->getReturnType(), Callee->getSourceRange());
230230

231-
if (FunctionDecl *Def = Callee->getDefinition()) {
232-
if (!Def->hasAttr<SYCLDeviceAttr>()) {
233-
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
234-
SemaRef.addSyclDeviceDecl(Def);
235-
}
236-
}
237231
if (auto const *FD = dyn_cast<FunctionDecl>(Callee)) {
238232
// FIXME: We need check all target specified attributes for error if
239233
// that function with attribute can not be called from sycl kernel. The
@@ -265,23 +259,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
265259
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
266260
for (const auto &Arg : E->arguments())
267261
CheckSYCLType(Arg->getType(), Arg->getSourceRange());
268-
269-
CXXConstructorDecl *Ctor = E->getConstructor();
270-
271-
if (FunctionDecl *Def = Ctor->getDefinition()) {
272-
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
273-
SemaRef.addSyclDeviceDecl(Def);
274-
}
275-
276-
const auto *ConstructedType = Ctor->getParent();
277-
if (ConstructedType->hasUserDeclaredDestructor()) {
278-
CXXDestructorDecl *Dtor = ConstructedType->getDestructor();
279-
280-
if (FunctionDecl *Def = Dtor->getDefinition()) {
281-
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
282-
SemaRef.addSyclDeviceDecl(Def);
283-
}
284-
}
285262
return true;
286263
}
287264

@@ -321,31 +298,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
321298
return true;
322299

323300
CheckSYCLType(E->getType(), E->getSourceRange());
324-
if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
325-
if (!VD->isLocalVarDeclOrParm() && VD->hasGlobalStorage()) {
326-
VD->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
327-
SemaRef.addSyclDeviceDecl(VD);
328-
}
329-
}
330-
return true;
331-
}
332-
333-
bool VisitCXXNewExpr(CXXNewExpr *E) {
334-
// Memory storage allocation is not allowed in kernels.
335-
// All memory allocation for the device is done on
336-
// the host using accessor classes. Consequently, the default
337-
// allocation operator new overloads that allocate
338-
// storage are disallowed in a SYCL kernel. The placement
339-
// new operator and any user-defined overloads that
340-
// do not allocate storage are permitted.
341-
if (FunctionDecl *FD = E->getOperatorNew()) {
342-
if (FunctionDecl *Def = FD->getDefinition()) {
343-
if (!Def->hasAttr<SYCLDeviceAttr>()) {
344-
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
345-
SemaRef.addSyclDeviceDecl(Def);
346-
}
347-
}
348-
}
349301
return true;
350302
}
351303

@@ -1389,13 +1341,8 @@ void Sema::MarkDevice(void) {
13891341
}
13901342
}
13911343
for (const auto &elt : Marker.KernelSet) {
1392-
if (FunctionDecl *Def = elt->getDefinition()) {
1393-
if (!Def->hasAttr<SYCLDeviceAttr>()) {
1394-
Def->addAttr(SYCLDeviceAttr::CreateImplicit(Context));
1395-
addSyclDeviceDecl(Def);
1396-
}
1344+
if (FunctionDecl *Def = elt->getDefinition())
13971345
Marker.TraverseStmt(Def->getBody());
1398-
}
13991346
}
14001347
}
14011348

@@ -1467,10 +1414,6 @@ static void emitCallToUndefinedFnDiag(Sema &SemaRef, const FunctionDecl *Callee,
14671414
if (Callee->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
14681415
return;
14691416

1470-
// Don't emit diagnostic for functions not called from device code
1471-
if (!Caller->hasAttr<SYCLDeviceAttr>() && !Caller->hasAttr<SYCLKernelAttr>())
1472-
return;
1473-
14741417
bool RedeclHasAttr = false;
14751418

14761419
for (const Decl *Redecl : Callee->redecls()) {

clang/test/CodeGenSYCL/device-indirectly-callable-attr.cpp

+57-4
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,63 @@ void foo() {
77
helper();
88
}
99

10-
// CHECK: define spir_func void @{{.*foo.*}}() #[[ATTRS_FOO:[0-9]+]]
10+
// CHECK: define spir_func void @{{.*foo.*}}() #[[ATTRS_INDIR_CALL:[0-9]+]]
1111
// CHECK: call spir_func void @{{.*helper.*}}()
1212
//
13-
// CHECK: define spir_func void @{{.*helper.*}}() #[[ATTRS_HELPER:[0-9]+]]
13+
// CHECK: define spir_func void @{{.*helper.*}}() #[[ATTRS_NOT_INDIR_CALL:[0-9]+]]
1414
//
15-
// CHECK: attributes #[[ATTRS_FOO]] = { {{.*}} "referenced-indirectly"
16-
// CHECK-NOT: attributes #[[ATTRS_HELPER]] = { {{.*}} "referenced-indirectly"
15+
16+
int bar20(int a) { return a + 20; }
17+
18+
class A {
19+
public:
20+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A3fooEv{{.*}}#[[ATTRS_INDIR_CALL]]
21+
// CHECK-DAG: define spir_func i32 @_Z5bar20{{.*}}#[[ATTRS_NOT_INDIR_CALL]]
22+
[[intel::device_indirectly_callable]] void foo() { bar20(10); }
23+
24+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AC1Ev{{.*}}#[[ATTRS_INDIR_CALL]]
25+
[[intel::device_indirectly_callable]] A() {}
26+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AD1Ev{{.*}}#[[ATTRS_INDIR_CALL]]
27+
[[intel::device_indirectly_callable]] ~A() {}
28+
29+
template <typename T>
30+
[[intel::device_indirectly_callable]] void AFoo(T t) {}
31+
32+
// Templates are emitted when they are instantiated
33+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A4AFooIiEEvT_{{.*}}#[[ATTRS_INDIR_CALL]]
34+
template <>
35+
[[intel::device_indirectly_callable]] void AFoo<int>(int t) {}
36+
};
37+
38+
struct Base {
39+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN4Base12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
40+
[[intel::device_indirectly_callable]] virtual void BaseWithAttr() { int a = 10; }
41+
virtual void BaseWithoutAttr() { int b = 20; }
42+
};
43+
44+
struct Overrider : Base {
45+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
46+
[[intel::device_indirectly_callable]] void BaseWithAttr() override { int a = 20; }
47+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider15BaseWithoutAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
48+
[[intel::device_indirectly_callable]] void BaseWithoutAttr() override { int b = 30; }
49+
};
50+
51+
struct Overrider1 : Base {
52+
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Overrider112BaseWithAttrEv
53+
void BaseWithAttr() override { int a = 20; }
54+
};
55+
56+
struct Finalizer : Base {
57+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
58+
[[intel::device_indirectly_callable]] void BaseWithAttr() final { int a = 20; }
59+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer15BaseWithoutAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
60+
[[intel::device_indirectly_callable]] void BaseWithoutAttr() final { int b = 30; }
61+
};
62+
63+
struct Finalizer1 : Base {
64+
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Finalizer112BaseWithAttrEv
65+
void BaseWithAttr() final { int a = 20; }
66+
};
67+
68+
// CHECK: attributes #[[ATTRS_INDIR_CALL]] = { {{.*}} "referenced-indirectly"
69+
// CHECK-NOT: attributes #[[ATTRS_NOT_INDIR_CALL]] = { {{.*}} "referenced-indirectly"

clang/test/CodeGenSYCL/sycl-device.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,84 @@
33

44
int bar(int b);
55

6+
int bar10(int a) { return a + 10; }
7+
int bar20(int a) { return a + 20; }
8+
9+
class A {
10+
public:
11+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A3fooEv
12+
// CHECK-DAG: define spir_func i32 @_Z5bar20i
13+
__attribute__((sycl_device)) void foo() { bar20(10); }
14+
15+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AC1Ev
16+
// CHECK-DAG: define spir_func i32 @_Z5bar10i
17+
__attribute__((sycl_device))
18+
A() { bar10(10); }
19+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AD1Ev
20+
__attribute__((sycl_device)) ~A() {}
21+
22+
template <typename T>
23+
__attribute__((sycl_device)) void AFoo(T t) {}
24+
25+
// Templates are emitted when they are instantiated
26+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A4AFooIiEEvT_
27+
template <>
28+
__attribute__((sycl_device)) void AFoo<int>(int t) {}
29+
30+
// CHECK-DAG: define linkonce_odr spir_func i32 @_ZN1A13non_annotatedEv
31+
int non_annotated() { return 1; }
32+
33+
// CHECK-DAG: define linkonce_odr spir_func i32 @_ZN1A9annotatedEv
34+
__attribute__((sycl_device)) int annotated() { return non_annotated() + 1; }
35+
};
36+
37+
template <typename T>
38+
struct B {
39+
T data;
40+
B(T _data) : data(_data) {}
41+
42+
__attribute__((sycl_device)) void BFoo(T t) {}
43+
};
44+
45+
template <>
46+
struct B<int> {
47+
int data;
48+
B(int _data) : data(_data) {}
49+
50+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1BIiE4BFooEi
51+
__attribute__((sycl_device)) void BFoo(int t) {}
52+
};
53+
54+
struct Base {
55+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN4Base12BaseWithAttrEv
56+
__attribute__((sycl_device)) virtual void BaseWithAttr() { int a = 10; }
57+
virtual void BaseWithoutAttr() { int b = 20; }
58+
};
59+
60+
struct Overrider : Base {
61+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider12BaseWithAttrEv
62+
__attribute__((sycl_device)) void BaseWithAttr() override { int a = 20; }
63+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider15BaseWithoutAttrEv
64+
__attribute__((sycl_device)) void BaseWithoutAttr() override { int b = 30; }
65+
};
66+
67+
struct Overrider1 : Base {
68+
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Overrider112BaseWithAttrEv
69+
void BaseWithAttr() override { int a = 20; }
70+
};
71+
72+
struct Finalizer : Base {
73+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer12BaseWithAttrEv
74+
__attribute__((sycl_device)) void BaseWithAttr() final { int a = 20; }
75+
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer15BaseWithoutAttrEv
76+
__attribute__((sycl_device)) void BaseWithoutAttr() final { int b = 30; }
77+
};
78+
79+
struct Finalizer1 : Base {
80+
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Finalizer112BaseWithAttrEv
81+
void BaseWithAttr() final { int a = 20; }
82+
};
83+
684
// CHECK-DAG: define spir_func i32 @_Z3fooii
785
__attribute__((sycl_device))
886
int foo(int a, int b) { return a + bar(b); }

clang/test/SemaSYCL/call-to-undefined-function.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ void forwardDeclFn() {
9292
}
9393

9494
int main() {
95+
// No problems in host code
96+
undefined();
97+
9598
kernel_single_task<class CallToUndefinedFnTester>([]() {
9699
// expected-note@-1 {{called by 'operator()'}}
97100
// expected-note@-2 {{called by 'operator()'}}

0 commit comments

Comments
 (0)