Copyright © 2023 Intel Corporation. All rights reserved.
Khronos® is a registered trademark and SYCL™ and SPIR™ are trademarks of The Khronos Group Inc. OpenCL™ is a trademark of Apple Inc. used by permission by Khronos.
To report problems with this extension, please open a new issue at:
This extension is written against the SYCL 2020 revision 8 specification. All references below to the "core SYCL specification" or to section numbers in the SYCL specification refer to that revision.
This extension references sections of the OpenCL specification version 3.0.14. References below to the "OpenCL specification" refer to that version. It also references sections of the OpenCL C specification version 3.0.14. References below to the "OpenCL C specification" refer to that version.
This extension also depends on the following other SYCL extensions:
This is an experimental extension specification, intended to provide early access to features and gather community feedback. Interfaces defined in this specification are implemented in DPC++, but they are not finalized and may change incompatibly in future versions of DPC++ without prior notice. Shipping software products should not rely on APIs defined in this specification.
This is an extension to sycl_ext_oneapi_kernel_compiler, which allows an application to define a kernel in the OpenCL C language when dynamically compiling a kernel from source.
This extension provides a feature-test macro as described in the core SYCL
specification.
An implementation supporting this extension must predefine the macro
SYCL_EXT_ONEAPI_KERNEL_COMPILER_OPENCL
to one of the values defined in the table below.
Applications can test for the existence of this macro to determine if the
implementation supports this feature, or applications can test the macro’s
value to determine which of the extension’s features the implementation
supports.
| Value | Description |
|---|---|
1 |
The APIs of this experimental extension are not versioned, so the feature-test macro always has this value. |
This extension adds the opencl enumerator to the source_language
enumeration, which indicates that a kernel bundle defines kernels in the
OpenCL C language.
namespace sycl::ext::oneapi::experimental {
enum class source_language : /*unspecified*/ {
// ...
opencl
};
} // namespace sycl::ext::oneapi::experimentalKernels written in the opencl language are text format.
As a result, the application must use the overload of
create_kernel_bundle_from_source taking std::string when creating a kernel
bundle from this language.
This extension adds the following type and constant definitions, which help identify the version of OpenCL and its extensions.
The meaning of the The constant values (e.g. [Note: The OpenCL C version is not the same as the the OpenCL version because some minor releases of OpenCL did not change the OpenCL C language. For example, there is no version of OpenCL C named "2.1" even though there is an OpenCL version named "2.1". — end note] |
This extension also adds the following member functions to the device class,
which allow the application to query which OpenCL features and extensions the
device supports.
Returns: The value |
|
Returns: The value |
|
Effects: If the device supports kernel bundles using the OpenCL extension
identified by Returns: The value |
|
Returns: If the device supports kernel bundles written in
|
The opencl language supports only those properties from
sycl_ext_oneapi_kernel_compiler that are common to all source languages.
The build_options property that is passed to the build function may contain
any of the compiler or linker options defined by the OpenCL specification,
except for those that are specific to creating an OpenCL library.
The kernel compiler can be used to create an OpenCL program, but not an OpenCL
library.
The -cl-std= option is required when compiling kernels that use OpenCL C 2.0
or OpenCL C 3.0 features.
Otherwise, the implementation defaults to the highest OpenCL C 1.x language
version that each device supports.
See section 5.8.6.5 "Options Controlling the OpenCL C version" of the OpenCL
specification for details.
OpenCL C kernel functions do not support C++ features like overloads or
templates.
As a result, the function name itself uniquely identifies the kernel function.
Therefore, the ext_oneapi_has_kernel and ext_oneapi_get_kernel member
functions identify a kernel using the function name, exactly as it appears in
the OpenCL C source code.
For example, if the kernel is defined this way in OpenCL C:
__kernel
void foo(__global int *in, __global int *out) {/*...*/}Then the application’s host code can query for the kernel like so:
sycl::kernel_bundle<sycl::bundle_state::executable> kb = /*...*/;
sycl::kernel k = kb.ext_oneapi_get_kernel("foo");The following table defines the set of OpenCL C kernel argument types that are supported by this extension and explains how to pass each type of argument from SYCL.
| OpenCL C type | Corresponding SYCL type |
|---|---|
One of the OpenCL C scalar types (e.g. |
A C++ type that is device copyable, which has the same width and data representation. [Note: Applications typically use the corresponding OpenCL type (e.g.
|
A |
Either a C++ pointer (typically a pointer to USM memory) or an |
A |
A [Note: The |
A class (or struct) passed by value. |
A C++ struct or class that is device copyable, which has the same size and data representation as the OpenCL C struct. [Note: The SYCL argument must not contain any |
When data allocated on the host is accessed by the kernel via a pointer, the
application must ensure that the data has the same size and representation on
the host and inside the OpenCL C kernel.
Applications can use the OpenCL types (e.g. cl_int) for this purpose.
A kernel object created from OpenCL C source code must be launched either as
a single-task kernel or as an nd-range kernel.
Attempting to launch such a kernel with a simple range iteration space results
in undefined behavior.
If the kernel is launched as a single-task kernel, it is executed with a
1-dimensional nd-range, with one work-group of one work-item.
Because it is launched as an nd-range kernel, the kernel can use features that
are normally prohibited in single-task kernels.
For example, the local_accessor type is allowed as a kernel argument, and the
kernel can use OpenCL C work-group collective functions and sub-group
functions.
Of course, these features have limited use because the kernel is launched with
just a single work-item.
If the kernel is launched as an nd-range kernel, the number of work-group
dimensions is the same as the number of dimensions in the nd_range.
The global size, local size, and the number of work-groups is determined in the
usual way from the nd_range.
If the OpenCL C kernel is decorated with the reqd_work_group_size attribute,
the local size in the nd_range must match this value.
The kernel may call the functions defined in section 6.15.1 "Work-Item
Functions" of the OpenCL C specification, with the following clarification.
Some of these functions take a dimindx parameter that selects a dimension
index.
This index has the opposite sense from SYCL, as described in section C.7.7
"OpenCL kernel conventions and SYCL" of the core SYCL specification.
To illustrate, consider a call to get_global_size from a kernel that is
invoked with a 3-dimensional nd_range.
Calling get_global_size(0) retrieves the global size from dimension 2 of the
nd_range, and calling get_global_size(2) retrieves the global size from
dimension 0 of the nd_range.
The following example shows a simple SYCL program that defines an OpenCL C kernel as a string and then compiles and launches it.
#include <sycl/sycl.hpp>
#include <CL/opencl.h>
namespace syclex = sycl::ext::oneapi::experimental;
int main() {
sycl::queue q;
// Kernel defined as an OpenCL C string. This could be dynamically
// generated instead of a literal.
std::string source = R"""(
__kernel void my_kernel(__global int *in, __global int *out) {
size_t i = get_global_id(0);
out[i] = in[i]*2 + 100;
}
)""";
sycl::kernel_bundle<sycl::bundle_state::ext_oneapi_source> kb_src =
syclex::create_kernel_bundle_from_source(
q.get_context(),
syclex::source_language::opencl,
source);
// Compile and link the kernel from the source definition.
sycl::kernel_bundle<sycl::bundle_state::executable> kb_exe =
syclex::build(kb_src);
// Get a "kernel" object representing the kernel defined in the
// source string.
sycl::kernel k = kb_exe.ext_oneapi_get_kernel("my_kernel");
constexpr int N = 4;
constexpr int WGSIZE = 1;
cl_int input[N] = {0, 1, 2, 3};
cl_int output[N] = {};
sycl::buffer inputbuf(input, sycl::range{N});
sycl::buffer outputbuf(output, sycl::range{N});
q.submit([&](sycl::handler &cgh) {
sycl::accessor in{inputbuf, cgh, sycl::read_only};
sycl::accessor out{outputbuf, cgh, sycl::read_write};
// Each argument to the kernel is a SYCL accessor.
cgh.set_args(in, out);
// Invoke the kernel over an nd-range.
sycl::nd_range ndr{{N}, {WGSIZE}};
cgh.parallel_for(ndr, k);
});
}This example demonstrates how to query the version of OpenCL C that is supported, how to query the supported features, and how to query the supported extensions.
#include <iostream>
#include <sycl/sycl.hpp>
namespace syclex = sycl::ext::oneapi::experimental;
int main() {
sycl::queue q;
sycl::device d = q.get_device();
if (d.ext_oneapi_can_build(syclex::source_language::opencl))
std::cout << "Device supports online compilation of OpenCL C kernels\n";
if (d.ext_oneapi_supports_cl_c_version(syclex::opencl_c_3_0))
std::cout << "Device supports online compilation with OpenCL C 3.0\n";
if (d.ext_oneapi_supports_cl_c_feature("__opencl_c_fp64"))
std::cout << "Device supports online compilation with 64-bit FP in OpenCL C\n";
syclex::cl_version version;
if (d.ext_oneapi_supports_cl_extension("cl_intel_bfloat16_conversions", &version)) {
std::cout << "Device supports online compilation of OpenCL C with bfloat16 "
"conversions (version: " << version.major << "." << version.minor << "." <<
version.patch << ")\n";
}
if (d.ext_oneapi_cl_profile() == "FULL_PROFILE")
std::cout << "Device supports online compilation with the OpenCL full profile\n";
}