Skip to content

Commit 4b4e732

Browse files
[SYCL] the first kernel function declaration should be added with attribute (#18405)
Docs [Defining a free function kernel](https://github.com/intel/llvm/blob/sycl/sycl/doc/extensions/proposed/sycl_ext_oneapi_free_function_kernels.asciidoc#defining-a-free-function-kernel) contains: The property must appear on the first declaration of the function in the translation unit. Redeclarations of the function may optionally be decorated with the same property if the property argument is the same. The effect is the same regardless of whether redeclarations are so decorated. This PR changes `isFreeFunction` to define if the first occurrence of kernel free function has attribute or not. New clang diagnostics was added to highlight that first occurrence does not have attribute but the next has.
1 parent 5e41b71 commit 4b4e732

File tree

4 files changed

+159
-7
lines changed

4 files changed

+159
-7
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12871,7 +12871,8 @@ def err_free_function_variadic_args : Error<
1287112871
"free function kernel cannot be a variadic function">;
1287212872
def err_free_function_return_type : Error<
1287312873
"SYCL free function kernel should have return type 'void'">;
12874-
12874+
def err_free_function_first_occurrence_missing_attr: Error<
12875+
"the first occurrence of SYCL kernel free function should be declared with 'sycl-nd-range-kernel' or 'sycl-single-task-kernel' compile time properties">;
1287512876

1287612877
// SYCL kernel entry point diagnostics
1287712878
def err_sycl_entry_point_invalid : Error<

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1159,14 +1159,33 @@ static target getAccessTarget(QualType FieldTy,
11591159
}
11601160

11611161
bool SemaSYCL::isFreeFunction(const FunctionDecl *FD) {
1162-
for (auto *IRAttr : FD->specific_attrs<SYCLAddIRAttributesFunctionAttr>()) {
1163-
SmallVector<std::pair<std::string, std::string>, 4> NameValuePairs =
1164-
IRAttr->getAttributeNameValuePairs(getASTContext());
1165-
for (const auto &NameValuePair : NameValuePairs) {
1166-
if (NameValuePair.first == "sycl-nd-range-kernel" ||
1167-
NameValuePair.first == "sycl-single-task-kernel") {
1162+
SourceLocation Loc = FD->getLocation();
1163+
bool NextDeclaredWithAttr = false;
1164+
for (FunctionDecl *Redecl : FD->redecls()) {
1165+
bool IsFreeFunctionAttr = false;
1166+
for (auto *IRAttr :
1167+
Redecl->specific_attrs<SYCLAddIRAttributesFunctionAttr>()) {
1168+
SmallVector<std::pair<std::string, std::string>, 4> NameValuePairs =
1169+
IRAttr->getAttributeNameValuePairs(getASTContext());
1170+
const auto it = std::find_if(
1171+
NameValuePairs.begin(), NameValuePairs.end(),
1172+
[](const auto &NameValuePair) {
1173+
return NameValuePair.first == "sycl-nd-range-kernel" ||
1174+
NameValuePair.first == "sycl-single-task-kernel";
1175+
});
1176+
IsFreeFunctionAttr = it != NameValuePairs.end();
1177+
}
1178+
if (Redecl->isFirstDecl()) {
1179+
if (IsFreeFunctionAttr)
11681180
return true;
1181+
if (NextDeclaredWithAttr) {
1182+
Diag(Loc, diag::err_free_function_first_occurrence_missing_attr);
1183+
Diag(Redecl->getLocation(), diag::note_previous_declaration);
1184+
return false;
11691185
}
1186+
} else {
1187+
Loc = Redecl->getLocation();
1188+
NextDeclaredWithAttr = IsFreeFunctionAttr;
11701189
}
11711190
}
11721191
return false;

clang/test/SemaSYCL/free_function_negative.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,36 @@ foo(int start, ...) { // expected-error {{free function kernel cannot be a varia
1010
foo1(int start, ...) { // expected-error {{free function kernel cannot be a variadic function}}
1111
}
1212

13+
// expected-note@+1 {{conflicting attribute is here}}
14+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 1)]] void
15+
foo2(int start);
16+
17+
// expected-error@+1 {{attribute 'add_ir_attributes_function' is already applied with different arguments}}
18+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
19+
foo2(int start) {
20+
}
21+
22+
// expected-note@+1 {{previous declaration is here}}
23+
void foo3(int start, int *ptr);
24+
25+
// expected-error@+2 {{the first occurrence of SYCL kernel free function should be declared with 'sycl-nd-range-kernel' or 'sycl-single-task-kernel' compile time properties}}
26+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
27+
foo3(int start, int *ptr){}
28+
29+
// expected-note@+1 {{previous declaration is here}}
30+
void foo4(float start, float *ptr);
31+
32+
// expected-error@+2 {{the first occurrence of SYCL kernel free function should be declared with 'sycl-nd-range-kernel' or 'sycl-single-task-kernel' compile time properties}}
33+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
34+
foo4(float start, float *ptr);
35+
36+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
37+
foo4(float start, float *ptr);
38+
39+
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
40+
foo4(float start, float *ptr){}
41+
42+
1343
// expected-error@+2 {{a function with a default argument value cannot be used to define SYCL free function kernel}}
1444
[[__sycl_detail__::add_ir_attributes_function("sycl-single-task-kernel", 2)]] void
1545
singleTaskKernelDefaultValues(int Value = 1) {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
// REQUIRES: aspect-usm_shared_allocations
2+
// RUN: %{build} -o %t.out
3+
// RUN: %{run} %t.out
4+
5+
// The name mangling for free function kernels currently does not work with PTX.
6+
// UNSUPPORTED: cuda, hip
7+
// UNSUPPORTED-INTENDED: Not implemented yet for Nvidia/AMD backends.
8+
9+
#include <iostream>
10+
#include <sycl/detail/core.hpp>
11+
#include <sycl/ext/oneapi/free_function_queries.hpp>
12+
#include <sycl/kernel_bundle.hpp>
13+
#include <sycl/usm.hpp>
14+
15+
namespace syclext = sycl::ext::oneapi;
16+
namespace syclexp = sycl::ext::oneapi::experimental;
17+
18+
static constexpr size_t NUM = 1024;
19+
static constexpr size_t WGSIZE = 16;
20+
21+
template <typename T> int check_result(T *ptr, T value) {
22+
for (size_t i = 0; i < NUM; ++i) {
23+
const T expected = value + static_cast<T>(i);
24+
if (ptr[i] != expected) {
25+
std::cout << "Kernel execution did not produce the expected result\n";
26+
return 1;
27+
}
28+
}
29+
return 0;
30+
}
31+
32+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
33+
void free_func(int *ptr, int start);
34+
35+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
36+
void free_func(int *ptr, int start);
37+
38+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
39+
void free_func1(int *ptr, int start);
40+
41+
void free_func1(int *ptr, int start);
42+
43+
template <typename T>
44+
static int call_kernel_code(sycl::queue &q, sycl::kernel &kernel, T value) {
45+
T *ptr = sycl::malloc_shared<T>(NUM, q);
46+
q.submit([&](sycl::handler &cgh) {
47+
if (value == 0)
48+
cgh.set_args(ptr);
49+
else
50+
cgh.set_args(ptr, value);
51+
sycl::nd_range ndr{{NUM}, {WGSIZE}};
52+
cgh.parallel_for(ndr, kernel);
53+
}).wait();
54+
const int ret = check_result(ptr, value);
55+
sycl::free(ptr, q);
56+
return ret;
57+
}
58+
59+
#define KERNEL_CODE(start, ptr, type) \
60+
size_t id = \
61+
syclext::this_work_item::get_nd_item<1>().get_global_linear_id(); \
62+
ptr[id] = static_cast<type>(start) + static_cast<type>(id);
63+
64+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
65+
void free_func(int *ptr, int start) { KERNEL_CODE(start, ptr, int); }
66+
67+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
68+
void free_func1(int *ptr, int start) { KERNEL_CODE(start, ptr, int); }
69+
70+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
71+
void free_func2(int *ptr, int start) { KERNEL_CODE(start, ptr, int); }
72+
73+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
74+
void free_func2(float *ptr, float start) { KERNEL_CODE(start, ptr, float); }
75+
76+
SYCL_EXT_ONEAPI_FUNCTION_PROPERTY((syclexp::nd_range_kernel<1>))
77+
void free_func2(int *ptr) { KERNEL_CODE(0, ptr, int); }
78+
79+
template <auto Func, typename T>
80+
int test_declarations(sycl::queue &q, sycl::context &ctxt, T value) {
81+
auto exe_bndl =
82+
syclexp::get_kernel_bundle<Func, sycl::bundle_state::executable>(ctxt);
83+
sycl::kernel k_func = exe_bndl.template ext_oneapi_get_kernel<Func>();
84+
return call_kernel_code<T>(q, k_func, value);
85+
}
86+
87+
int main() {
88+
sycl::queue q;
89+
sycl::context ctxt = q.get_context();
90+
91+
int result{0};
92+
result |= test_declarations<free_func, int>(q, ctxt, 3);
93+
result |= test_declarations<free_func1, int>(q, ctxt, 3);
94+
result |=
95+
test_declarations<static_cast<void (*)(int *, int)>(free_func2), int>(
96+
q, ctxt, 3);
97+
result |= test_declarations<static_cast<void (*)(float *, float)>(free_func2),
98+
float>(q, ctxt, 3.14f);
99+
result |= test_declarations<static_cast<void (*)(int *)>(free_func2), int>(
100+
q, ctxt, 0);
101+
return result;
102+
}

0 commit comments

Comments
 (0)