Skip to content

Commit a65b499

Browse files
sndmitrievbader
authored andcommitted
[SYCL] Add support for SYCL_EXTERNAL (#622)
- Complete implementation for sycl_device attribute All functions annotated by the sycl_device attribute are supposed to be retained in the device compilation as well as the functions that are called/accessed from them. Support for sycl_device attribute was incomplete in clang; this patch adds the missing functionality. - Define SYCL_EXTERNAL macro as prescribed by the spec Signed-off-by: Sergey Dmitriev <[email protected]>
1 parent 98d9382 commit a65b499

File tree

12 files changed

+229
-16
lines changed

12 files changed

+229
-16
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1025,9 +1025,9 @@ def CUDAShared : InheritableAttr {
10251025

10261026
def SYCLDevice : InheritableAttr {
10271027
let Spellings = [GNU<"sycl_device">];
1028-
let Subjects = SubjectList<[Function, Var]>;
1028+
let Subjects = SubjectList<[Function]>;
10291029
let LangOpts = [SYCLIsDevice];
1030-
let Documentation = [Undocumented];
1030+
let Documentation = [SYCLDeviceDocs];
10311031
}
10321032

10331033
def SYCLKernel : InheritableAttr {

clang/include/clang/Basic/AttrDocs.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1845,6 +1845,17 @@ function pointer for the specified function.
18451845
}];
18461846
}
18471847

1848+
def SYCLDeviceDocs : Documentation {
1849+
let Category = DocCatFunction;
1850+
let Heading = "sycl_device";
1851+
let Content = [{
1852+
This attribute can only be applied to functions and indicates that the
1853+
function must be treated as a device function and must be emitted even if it has
1854+
no direct uses from other device functions. All ``sycl_device`` function callees
1855+
implicitly inherit this attribute.
1856+
}];
1857+
}
1858+
18481859
def RISCVInterruptDocs : Documentation {
18491860
let Category = DocCatFunction;
18501861
let Heading = "interrupt (RISCV)";

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9803,10 +9803,12 @@ def err_sycl_non_std_layout_type : Error<
98039803
"kernel parameter has non-standard layout class/struct type">;
98049804
def err_conflicting_sycl_kernel_attributes : Error<
98059805
"conflicting attributes applied to a SYCL kernel">;
9806-
def err_sycl_device_indirectly_callable_cannot_be_applied_here
9807-
: Error<"device_indirectly_callable attribute cannot be applied to a "
9806+
def err_sycl_attibute_cannot_be_applied_here
9807+
: Error<"%0 attribute cannot be applied to a "
98089808
"%select{static function or function in an anonymous namespace"
9809-
"|class member function}0">;
9809+
"|class member function"
9810+
"|function with a raw pointer return type"
9811+
"|function with a raw pointer parameter type}1">;
98109812

98119813
def err_bit_cast_non_trivially_copyable : Error<
98129814
"__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;

clang/lib/Frontend/InitPreprocessor.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ static void InitializePredefinedMacros(const TargetInfo &TI,
10741074
// SYCL device compiler which doesn't produce host binary.
10751075
if (LangOpts.SYCLIsDevice) {
10761076
Builder.defineMacro("__SYCL_DEVICE_ONLY__", "1");
1077+
Builder.defineMacro("SYCL_EXTERNAL", "__attribute__((sycl_device))");
10771078
if (!getenv("DISABLE_INFER_AS"))
10781079
Builder.defineMacro("__SYCL_ENABLE_INFER_AS__", "1");
10791080
}

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4417,19 +4417,45 @@ static void handleOptimizeNoneAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
44174417
D->addAttr(Optnone);
44184418
}
44194419

4420+
static void handleSYCLDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
4421+
auto *FD = cast<FunctionDecl>(D);
4422+
if (!FD->isExternallyVisible()) {
4423+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4424+
<< AL << 0 /* static function or anonymous namespace */;
4425+
return;
4426+
}
4427+
if (isa<CXXMethodDecl>(FD)) {
4428+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4429+
<< AL << 1 /* class member function */;
4430+
return;
4431+
}
4432+
if (FD->getReturnType()->isPointerType()) {
4433+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4434+
<< AL << 2 /* function with a raw pointer return type */;
4435+
return;
4436+
}
4437+
for (const ParmVarDecl *Param : FD->parameters())
4438+
if (Param->getType()->isPointerType()) {
4439+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4440+
<< AL << 3 /* function with a raw pointer parameter type */;
4441+
return;
4442+
}
4443+
4444+
S.addSyclDeviceDecl(D);
4445+
handleSimpleAttribute<SYCLDeviceAttr>(S, D, AL);
4446+
}
4447+
44204448
static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D,
44214449
const ParsedAttr &AL) {
44224450
auto *FD = cast<FunctionDecl>(D);
44234451
if (!FD->isExternallyVisible()) {
4424-
S.Diag(AL.getLoc(),
4425-
diag::err_sycl_device_indirectly_callable_cannot_be_applied_here)
4426-
<< 0 /* static function or anonymous namespace */;
4452+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4453+
<< AL << 0 /* static function or anonymous namespace */;
44274454
return;
44284455
}
44294456
if (isa<CXXMethodDecl>(FD)) {
4430-
S.Diag(AL.getLoc(),
4431-
diag::err_sycl_device_indirectly_callable_cannot_be_applied_here)
4432-
<< 1 /* class member function */;
4457+
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
4458+
<< AL << 1 /* class member function */;
44334459
return;
44344460
}
44354461

@@ -7116,6 +7142,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
71167142
case ParsedAttr::AT_SYCLKernel:
71177143
handleSimpleAttribute<SYCLKernelAttr>(S, D, AL);
71187144
break;
7145+
case ParsedAttr::AT_SYCLDevice:
7146+
handleSYCLDeviceAttr(S, D, AL);
7147+
break;
71197148
case ParsedAttr::AT_SYCLDeviceIndirectlyCallable:
71207149
handleSYCLDeviceIndirectlyCallableAttr(S, D, AL);
71217150
break;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// RUN: %clang_cc1 -triple spir64-unknown-linux-sycldevice -std=c++11 -fsycl-is-device -disable-llvm-passes -S -emit-llvm -x c++ %s -o - | FileCheck %s
2+
// Test code generation for sycl_device attribute.
3+
4+
int bar(int b);
5+
6+
// CHECK-DAG: define spir_func i32 @_Z3fooii
7+
__attribute__((sycl_device))
8+
int foo(int a, int b) { return a + bar(b); }
9+
10+
// CHECK-DAG: define spir_func i32 @_Z3bari
11+
int bar(int b) { return b; }
12+
13+
// CHECK-DAG: define spir_func i32 @_Z3fari
14+
int far(int b) { return b; }
15+
16+
// CHECK-DAG: define spir_func i32 @_Z3booii
17+
__attribute__((sycl_device))
18+
int boo(int a, int b) { return a + far(b); }
19+
20+
// CHECK-DAG: define spir_func i32 @_Z3cari
21+
__attribute__((sycl_device))
22+
int car(int b);
23+
int car(int b) { return b; }
24+
25+
// CHECK-DAG: define spir_func i32 @_Z3cazi
26+
int caz(int b);
27+
__attribute__((sycl_device))
28+
int caz(int b) { return b; }
29+
30+
template<typename T>
31+
__attribute__((sycl_device))
32+
void taf(T t) {}
33+
34+
// CHECK-DAG: define weak_odr spir_func void @_Z3tafIiEvT_
35+
template void taf<int>(int t);
36+
37+
// CHECK-DAG: define spir_func void @_Z3tafIcEvT_
38+
template<> void taf<char>(char t) {}
39+
40+
template<typename T>
41+
void tar(T t) {}
42+
43+
// CHECK-DAG: define spir_func void @_Z3tarIcEvT_
44+
template<>
45+
__attribute__((sycl_device))
46+
void tar<char>(char t) {}
47+
48+
// CHECK-NOT: @_Z3tarIiEvT_
49+
template void tar<int>(int t);
50+
51+
// CHECK-NOT: @_Z3gooi
52+
int goo(int b) { return b; }

clang/test/Misc/pragma-attribute-supported-attributes-list.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
// CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
127127
// CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
128128
// CHECK-NEXT: ReturnsTwice (SubjectMatchRule_function)
129-
// CHECK-NEXT: SYCLDevice (SubjectMatchRule_function, SubjectMatchRule_variable)
129+
// CHECK-NEXT: SYCLDevice (SubjectMatchRule_function)
130130
// CHECK-NEXT: SYCLDeviceIndirectlyCallable (SubjectMatchRule_function)
131131
// CHECK-NEXT: SYCLKernel (SubjectMatchRule_function)
132132
// CHECK-NEXT: ScopedLockable (SubjectMatchRule_record)

clang/test/Preprocessor/sycl-macro.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
// RUN: %clang_cc1 %s -fsycl -E -dM | FileCheck --check-prefix=CHECK-ANY-SYCL %s
44
// RUN: %clang_cc1 %s -fsycl-is-device -E -dM -fms-compatibility | FileCheck --check-prefix=CHECK-MSVC %s
55
// CHECK-NOT:#define __SYCL_DEVICE_ONLY__ 1
6+
// CHECK-NOT:#define SYCL_EXTERNAL
67
// CHECK-NOT:#define CL_SYCL_LANGUAGE_VERSION 121
78
// CHECK-ANY-SYCL-NOT:#define __SYCL_DEVICE_ONLY__ 1
89
// CHECK-ANY-SYCL:#define CL_SYCL_LANGUAGE_VERSION 121
910
// CHECK-SYCL:#define CL_SYCL_LANGUAGE_VERSION 121
11+
// CHECK-SYCL:#define SYCL_EXTERNAL __attribute__((sycl_device))
1012
// CHECK-MSVC-NOT: __GNUC__
1113
// CHECK-MSVC-NOT: __STDC__

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ int N;
1010
[[intel::device_indirectly_callable(3)]] // expected-error {{'device_indirectly_callable' attribute takes no arguments}}
1111
void bar() {}
1212

13-
[[intel::device_indirectly_callable]] // expected-error {{device_indirectly_callable attribute cannot be applied to a static function or function in an anonymous namespace}}
13+
[[intel::device_indirectly_callable]] // expected-error {{'device_indirectly_callable' attribute cannot be applied to a static function or function in an anonymous namespace}}
1414
static void func1() {}
1515

1616
namespace {
17-
[[intel::device_indirectly_callable]] // expected-error {{device_indirectly_callable attribute cannot be applied to a static function or function in an anonymous namespace}}
17+
[[intel::device_indirectly_callable]] // expected-error {{'device_indirectly_callable' attribute cannot be applied to a static function or function in an anonymous namespace}}
1818
void func2() {}
1919
}
2020

2121
class A {
22-
[[intel::device_indirectly_callable]] // expected-error {{device_indirectly_callable attribute cannot be applied to a class member function}}
22+
[[intel::device_indirectly_callable]] // expected-error {{'device_indirectly_callable' attribute cannot be applied to a class member function}}
2323
A() {}
2424

25-
[[intel::device_indirectly_callable]] // expected-error {{device_indirectly_callable attribute cannot be applied to a class member function}}
25+
[[intel::device_indirectly_callable]] // expected-error {{'device_indirectly_callable' attribute cannot be applied to a class member function}}
2626
int func3() {}
2727
};
2828

clang/test/SemaSYCL/sycl-device.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -fsyntax-only -verify %s
2+
// RUN: %clang_cc1 -verify -DNO_SYCL %s
3+
4+
#ifndef NO_SYCL
5+
6+
__attribute__((sycl_device)) // expected-warning {{'sycl_device' attribute only applies to functions}}
7+
int N;
8+
9+
__attribute__((sycl_device(3))) // expected-error {{'sycl_device' attribute takes no arguments}}
10+
void bar() {}
11+
12+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a static function or function in an anonymous namespace}}
13+
static void func1() {}
14+
15+
namespace {
16+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a static function or function in an anonymous namespace}}
17+
void func2() {}
18+
}
19+
20+
class A {
21+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a class member function}}
22+
A() {}
23+
24+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a class member function}}
25+
int func3() {}
26+
};
27+
28+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a function with a raw pointer return type}}
29+
int* func3() { return nullptr; }
30+
31+
__attribute__((sycl_device)) // expected-error {{'sycl_device' attribute cannot be applied to a function with a raw pointer parameter type}}
32+
void func3(int *) {}
33+
34+
#else
35+
36+
__attribute__((sycl_device)) // expected-warning {{'sycl_device' attribute ignored}}
37+
void baz() {}
38+
39+
#endif // NO_SYCL

sycl/include/CL/sycl/detail/common.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ static inline std::string codeToString(cl_int code){
8888
#define ALWAYS_INLINE
8989
#endif
9090

91+
#ifndef SYCL_EXTERNAL
92+
#define SYCL_EXTERNAL
93+
#endif
94+
9195
namespace cl {
9296
namespace sycl {
9397
namespace detail {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Test1 - check that kernel can call a SYCL_EXTERNAL function defined in a
2+
// different object file.
3+
// RUN: %clangxx -fsycl -DSOURCE1 -c %s -o %t1.o
4+
// RUN: %clangxx -fsycl -DSOURCE2 -c %s -o %t2.o
5+
// RUN: %clangxx -fsycl %t1.o %t2.o -o %t.exe
6+
// RUN: %CPU_RUN_PLACEHOLDER %t.exe
7+
// RUN: %GPU_RUN_PLACEHOLDER %t.exe
8+
// RUN: %ACC_RUN_PLACEHOLDER %t.exe
9+
//
10+
// Test2 - check that kernel can call a SYCL_EXTERNAL function defined in a
11+
// static library.
12+
// RUN: rm -f %t.a
13+
// RUN: llvm-ar crv %t.a %t1.o
14+
// RUN: %clangxx -fsycl %t2.o -foffload-static-lib=%t.a -o %t.exe
15+
// RUN: %CPU_RUN_PLACEHOLDER %t.exe
16+
// RUN: %GPU_RUN_PLACEHOLDER %t.exe
17+
// RUN: %ACC_RUN_PLACEHOLDER %t.exe
18+
19+
#include <CL/sycl.hpp>
20+
#include <iostream>
21+
22+
#ifdef SOURCE1
23+
int bar(int b);
24+
25+
SYCL_EXTERNAL
26+
int foo(int a, int b) {
27+
return a + bar(b);
28+
}
29+
30+
int bar(int b) {
31+
return b + 5;
32+
}
33+
#endif // SOURCE1
34+
35+
#ifdef SOURCE2
36+
SYCL_EXTERNAL
37+
int foo(int A, int B);
38+
39+
int main(void) {
40+
constexpr unsigned Size = 4;
41+
int A[Size] = {1, 2, 3, 4};
42+
int B[Size] = {1, 2, 3, 4};
43+
int C[Size];
44+
45+
{
46+
cl::sycl::range<1> range{Size};
47+
cl::sycl::buffer<int, 1> bufA(A, range);
48+
cl::sycl::buffer<int, 1> bufB(B, range);
49+
cl::sycl::buffer<int, 1> bufC(C, range);
50+
51+
cl::sycl::queue().submit([&](cl::sycl::handler &cgh) {
52+
auto accA = bufA.get_access<cl::sycl::access::mode::read>(cgh);
53+
auto accB = bufB.get_access<cl::sycl::access::mode::read>(cgh);
54+
auto accC = bufC.get_access<cl::sycl::access::mode::write>(cgh);
55+
56+
cgh.parallel_for<class Test>(range, [=](cl::sycl::id<1> ID) {
57+
accC[ID] = foo(accA[ID], accB[ID]);
58+
});
59+
});
60+
}
61+
62+
for (unsigned I = 0; I < Size; ++I) {
63+
int Ref = foo(A[I], B[I]);
64+
if (C[I] != Ref) {
65+
std::cout << "fail: [" << I << "] == " << C[I] << ", expected " << Ref
66+
<< "\n";
67+
return 1;
68+
}
69+
}
70+
std::cout << "pass\n";
71+
return 0;
72+
}
73+
#endif // SOURCE2

0 commit comments

Comments
 (0)