A SYCL kernel entry point function is a non-member function or a static
member function declared with the `sycl_kernel_entry_point` attribute.
Such functions define a pattern for an offload kernel entry point
function to be generated to enable execution of a SYCL kernel on a
device. A SYCL library implementation orchestrates the invocation of
these functions with corresponding SYCL kernel arguments in response to
calls to SYCL kernel invocation functions specified by the SYCL 2020
specification.
The offload kernel entry point function (sometimes referred to as the
SYCL kernel caller function) is generated from the SYCL kernel entry
point function by a transformation of the function parameters followed
by a transformation of the function body to replace references to the
original parameters with references to the transformed ones. Exactly how
parameters are transformed will be explained in a future change that
implements non-trivial transformations. For now, it suffices to state
that a given parameter of the SYCL kernel entry point function may be
transformed to multiple parameters of the offload kernel entry point as
needed to satisfy offload kernel argument passing requirements.
Parameters that are decomposed in this way are reconstituted as local
variables in the body of the generated offload kernel entry point
function.
For example, given the following SYCL kernel entry point function
definition:
```
template<typename KernelNameType, typename KernelType>
[[clang::sycl_kernel_entry_point(KernelNameType)]]
void sycl_kernel_entry_point(KernelType kernel) {
kernel();
}
```
and the following call:
```
struct Kernel {
int dm1;
int dm2;
void operator()() const;
};
Kernel k;
sycl_kernel_entry_point<class kernel_name>(k);
```
the corresponding offload kernel entry point function that is generated
might look as follows (assuming `Kernel` is a type that requires
decomposition):
```
void offload_kernel_entry_point_for_kernel_name(int dm1, int dm2) {
Kernel kernel{dm1, dm2};
kernel();
}
```
Other details of the generated offload kernel entry point function, such
as its name and calling convention, are implementation details that need
not be reflected in the AST and may differ across target devices. For
that reason, only the transformation described above is represented in
the AST; other details will be filled in during code generation.
These transformations are represented using new AST nodes introduced
with this change. `OutlinedFunctionDecl` holds a sequence of
`ImplicitParamDecl` nodes and a sequence of statement nodes that
correspond to the transformed parameters and function body.
`SYCLKernelCallStmt` wraps the original function body and associates it
with an `OutlinedFunctionDecl` instance. For the example above, the AST
generated for the `sycl_kernel_entry_point<kernel_name>` specialization
would look as follows:
```
FunctionDecl 'sycl_kernel_entry_point<kernel_name>(Kernel)'
TemplateArgument type 'kernel_name'
TemplateArgument type 'Kernel'
ParmVarDecl kernel 'Kernel'
SYCLKernelCallStmt
CompoundStmt
<original statements>
OutlinedFunctionDecl
ImplicitParamDecl 'dm1' 'int'
ImplicitParamDecl 'dm2' 'int'
CompoundStmt
VarDecl 'kernel' 'Kernel'
<initialization of 'kernel' with 'dm1' and 'dm2'>
<transformed statements with redirected references of 'kernel'>
```
Any ODR-use of the SYCL kernel entry point function will (with future
changes) suffice for the offload kernel entry point to be emitted. An
actual call to the SYCL kernel entry point function will result in a
call to the function. However, evaluation of a `SYCLKernelCallStmt`
statement is a no-op, so such calls will have no effect other than to
trigger emission of the offload kernel entry point.
Additionally, as a related change inspired by code review feedback,
these changes disallow use of the `sycl_kernel_entry_point` attribute
with functions defined with a _function-try-block_. The SYCL 2020
specification prohibits the use of C++ exceptions in device functions.
Even if exceptions were not prohibited, it is unclear what the semantics
would be for an exception that escapes the SYCL kernel entry point
function; the boundary between host and device code could be an implicit
noexcept boundary that results in program termination if violated, or
the exception could perhaps be propagated to host code via the SYCL
library. Pending support for C++ exceptions in device code and clear
semantics for handling them at the host-device boundary, this change
makes use of the `sycl_kernel_entry_point` attribute with a function
defined with a _function-try-block_ an error.
362 lines
12 KiB
C++
362 lines
12 KiB
C++
// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++17 -fsyntax-only -fsycl-is-device -verify %s
|
|
// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++20 -fsyntax-only -fsycl-is-device -verify %s
|
|
// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -fsyntax-only -fsycl-is-device -verify %s
|
|
|
|
// These tests validate appertainment for the sycl_kernel_entry_point attribute.
|
|
|
|
#if __cplusplus >= 202002L
|
|
// Mock coroutine support.
|
|
namespace std {
|
|
|
|
template<typename Promise = void>
|
|
struct coroutine_handle {
|
|
template<typename T>
|
|
coroutine_handle(const coroutine_handle<T>&);
|
|
static coroutine_handle from_address(void *addr);
|
|
};
|
|
|
|
template<typename R, typename... Args>
|
|
struct coroutine_traits {
|
|
struct suspend_never {
|
|
bool await_ready() const noexcept;
|
|
void await_suspend(std::coroutine_handle<>) const noexcept;
|
|
void await_resume() const noexcept;
|
|
};
|
|
struct promise_type {
|
|
void get_return_object() noexcept;
|
|
suspend_never initial_suspend() const noexcept;
|
|
suspend_never final_suspend() const noexcept;
|
|
void return_void() noexcept;
|
|
void unhandled_exception() noexcept;
|
|
};
|
|
};
|
|
|
|
}
|
|
#endif
|
|
|
|
// A unique kernel name type is required for each declared kernel entry point.
|
|
template<int, int = 0> struct KN;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Valid declarations.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Function declaration with GNU attribute spelling
|
|
__attribute__((sycl_kernel_entry_point(KN<1>)))
|
|
void ok1();
|
|
|
|
// Function declaration with Clang attribute spelling.
|
|
[[clang::sycl_kernel_entry_point(KN<2>)]]
|
|
void ok2();
|
|
|
|
// Function definition.
|
|
[[clang::sycl_kernel_entry_point(KN<3>)]]
|
|
void ok3() {}
|
|
|
|
// Function template definition.
|
|
template<typename KNT, typename T>
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
void ok4(T) {}
|
|
|
|
// Function template explicit specialization.
|
|
template<>
|
|
[[clang::sycl_kernel_entry_point(KN<4,1>)]]
|
|
void ok4<KN<4,1>>(int) {}
|
|
|
|
// Function template explicit instantiation.
|
|
template void ok4<KN<4,2>, long>(long);
|
|
|
|
namespace NS {
|
|
// Function declaration at namespace scope.
|
|
[[clang::sycl_kernel_entry_point(KN<5>)]]
|
|
void ok5();
|
|
}
|
|
|
|
struct S6 {
|
|
// Static member function declaration.
|
|
[[clang::sycl_kernel_entry_point(KN<6>)]]
|
|
static void ok6();
|
|
};
|
|
|
|
// Dependent hidden friend definition.
|
|
template<typename KNT>
|
|
struct S7 {
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend void ok7(S7) {}
|
|
};
|
|
void test_ok7() {
|
|
ok7(S7<KN<7>>{});
|
|
}
|
|
|
|
// Non-dependent hidden friend definition.
|
|
struct S8Base {};
|
|
template<typename>
|
|
struct S8 : S8Base {
|
|
[[clang::sycl_kernel_entry_point(KN<8>)]]
|
|
friend void ok8(const S8Base&) {}
|
|
};
|
|
void test_ok8() {
|
|
ok8(S8<int>{});
|
|
}
|
|
|
|
// The sycl_kernel_entry_point attribute must match across declarations and
|
|
// cannot be added for the first time after a definition.
|
|
[[clang::sycl_kernel_entry_point(KN<9>)]]
|
|
void ok9();
|
|
[[clang::sycl_kernel_entry_point(KN<9>)]]
|
|
void ok9();
|
|
[[clang::sycl_kernel_entry_point(KN<10>)]]
|
|
void ok10();
|
|
void ok10() {}
|
|
void ok11();
|
|
[[clang::sycl_kernel_entry_point(KN<11>)]]
|
|
void ok11() {}
|
|
|
|
using VOID = void;
|
|
[[clang::sycl_kernel_entry_point(KN<12>)]]
|
|
VOID ok12();
|
|
[[clang::sycl_kernel_entry_point(KN<13>)]]
|
|
const void ok13();
|
|
|
|
#if __cplusplus >= 202302L
|
|
auto ok14 = [] [[clang::sycl_kernel_entry_point(KN<14>)]] static -> void {};
|
|
#endif
|
|
|
|
template<typename KNT, typename T>
|
|
struct S15 {
|
|
// Don't diagnose a dependent return type as a non-void type.
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
static T ok15();
|
|
};
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Invalid declarations.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// The sycl_kernel_entry_point attribute cannot appertain to main() because
|
|
// main() has a non-void return type. However, if the requirement for a void
|
|
// return type were to be relaxed or if an allowance was made for main() to
|
|
// return void (as gcc allows in some modes and as has been proposed to WG21
|
|
// on occassion), main() still can't function as a SYCL kernel entry point,
|
|
// so this test ensures such attempted uses of the attribute are rejected.
|
|
struct Smain;
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a 'void' return type}}
|
|
[[clang::sycl_kernel_entry_point(Smain)]]
|
|
int main();
|
|
|
|
template<int> struct BADKN;
|
|
|
|
struct B1 {
|
|
// Non-static data member declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<1>)]]
|
|
int bad1;
|
|
};
|
|
|
|
struct B2 {
|
|
// Static data member declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<2>)]]
|
|
static int bad2;
|
|
};
|
|
|
|
struct B3 {
|
|
// Non-static member function declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<3>)]]
|
|
void bad3();
|
|
};
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
namespace [[clang::sycl_kernel_entry_point(BADKN<4>)]] bad4 {}
|
|
|
|
#if __cplusplus >= 202002L
|
|
// expected-error@+2 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
template<typename>
|
|
concept bad5 [[clang::sycl_kernel_entry_point(BADKN<5>)]] = true;
|
|
#endif
|
|
|
|
// Type alias declarations.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
typedef void bad6 [[clang::sycl_kernel_entry_point(BADKN<6>)]] ();
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
using bad7 [[clang::sycl_kernel_entry_point(BADKN<7>)]] = void();
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
using bad8 [[clang::sycl_kernel_entry_point(BADKN<8>)]] = int;
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to types}}
|
|
using bad9 = int [[clang::sycl_kernel_entry_point(BADKN<9>)]];
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to types}}
|
|
using bad10 = int() [[clang::sycl_kernel_entry_point(BADKN<10>)]];
|
|
|
|
// Variable declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<11>)]]
|
|
int bad11;
|
|
|
|
// Class declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
struct [[clang::sycl_kernel_entry_point(BADKN<12>)]] bad12;
|
|
|
|
// Enumeration declaration.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
enum [[clang::sycl_kernel_entry_point(BADKN<13>)]] bad13 {};
|
|
|
|
// Enumerator.
|
|
// expected-error@+2 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
enum {
|
|
bad14 [[clang::sycl_kernel_entry_point(BADKN<14>)]]
|
|
};
|
|
|
|
// Attribute added after the definition.
|
|
// expected-error@+3 {{'sycl_kernel_entry_point' attribute cannot be added to a function after the function is defined}}
|
|
// expected-note@+1 {{previous definition is here}}
|
|
void bad15() {}
|
|
[[clang::sycl_kernel_entry_point(BADKN<15>)]]
|
|
void bad15();
|
|
|
|
// The function must return void.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a 'void' return type}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<16>)]]
|
|
int bad16();
|
|
|
|
// Function parameters.
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
void bad17(void (fp [[clang::sycl_kernel_entry_point(BADKN<17>)]])());
|
|
|
|
// Function template parameters.
|
|
// FIXME: Clang currently ignores attributes that appear in template parameters
|
|
// FIXME: and the C++ standard is unclear regarding whether such attributes are
|
|
// FIXME: permitted. P3324 (Attributes for namespace aliases, template
|
|
// FIXME: parameters, and lambda captures) seeks to clarify the situation.
|
|
// FIXME-expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions}}
|
|
template<void (fp [[clang::sycl_kernel_entry_point(BADKN<18>)]])()>
|
|
void bad18();
|
|
|
|
#if __cplusplus >= 202002L
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<19>)]]
|
|
void bad19() {
|
|
co_return;
|
|
}
|
|
#endif
|
|
|
|
struct B20 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<20>)]]
|
|
B20();
|
|
};
|
|
|
|
struct B21 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<21>)]]
|
|
~B21();
|
|
};
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a variadic function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<22>)]]
|
|
void bad22(...);
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a deleted function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<23>)]]
|
|
void bad23() = delete;
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a constexpr function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<24>)]]
|
|
constexpr void bad24() {}
|
|
|
|
#if __cplusplus >= 202002L
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a consteval function}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<25>)]]
|
|
consteval void bad25() {}
|
|
#endif
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function declared with the 'noreturn' attribute}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<26>)]]
|
|
[[noreturn]] void bad26();
|
|
|
|
// expected-error@+3 {{attribute 'target' multiversioning cannot be combined with attribute 'sycl_kernel_entry_point'}}
|
|
__attribute__((target("avx"))) void bad27();
|
|
[[clang::sycl_kernel_entry_point(BADKN<27>)]]
|
|
__attribute__((target("sse4.2"))) void bad27();
|
|
|
|
template<typename KNT>
|
|
struct B28 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a deleted function}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend void bad28() = delete;
|
|
};
|
|
|
|
#if __cplusplus >= 202002L
|
|
template<typename KNT, typename T>
|
|
struct B29 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a defaulted function}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend T operator==(B29, B29) = default;
|
|
};
|
|
#endif
|
|
|
|
#if __cplusplus >= 202002L
|
|
template<typename KNT>
|
|
struct B30 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend void bad30() { co_return; }
|
|
};
|
|
#endif
|
|
|
|
template<typename KNT>
|
|
struct B31 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a variadic function}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend void bad31(...) {}
|
|
};
|
|
|
|
template<typename KNT>
|
|
struct B32 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a constexpr function}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend constexpr void bad32() {}
|
|
};
|
|
|
|
#if __cplusplus >= 202002L
|
|
template<typename KNT>
|
|
struct B33 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a consteval function}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
friend consteval void bad33() {}
|
|
};
|
|
#endif
|
|
|
|
template<typename KNT>
|
|
struct B34 {
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function declared with the 'noreturn' attribute}}
|
|
[[clang::sycl_kernel_entry_point(KNT)]]
|
|
[[noreturn]] friend void bad34() {}
|
|
};
|
|
|
|
#if __cplusplus >= 202302L
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a non-static member function}}
|
|
auto bad35 = [] [[clang::sycl_kernel_entry_point(BADKN<35>)]] -> void {};
|
|
#endif
|
|
|
|
#if __cplusplus >= 202302L
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute only applies to functions with a non-deduced 'void' return type}}
|
|
auto bad36 = [] [[clang::sycl_kernel_entry_point(BADKN<36>)]] static {};
|
|
#endif
|
|
|
|
#if __cplusplus >= 202302L
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a coroutine}}
|
|
auto bad37 = [] [[clang::sycl_kernel_entry_point(BADKN<37>)]] static -> void { co_return; };
|
|
#endif
|
|
|
|
// expected-error@+1 {{'sycl_kernel_entry_point' attribute cannot be applied to a function defined with a function try block}}
|
|
[[clang::sycl_kernel_entry_point(BADKN<38>)]]
|
|
void bad38() try {} catch(...) {}
|
|
|
|
// expected-error@+2 {{'sycl_kernel_entry_point' attribute cannot be applied to a function defined with a function try block}}
|
|
template<typename>
|
|
[[clang::sycl_kernel_entry_point(BADKN<39>)]]
|
|
void bad39() try {} catch(...) {}
|