-
Notifications
You must be signed in to change notification settings - Fork 733
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SYCL] Implement OpenCL kernel function generation
Summary: All SYCL memory objects shared between host and device (buffers/images, these objects map to OpenCL buffers and images) must be accessed through special accessor classes. The "device" side implementation of these classes contain pointers to the device memory. As there is no way in OpenCL to pass structures with pointers inside as kernel arguments, all memory objects shared between host and device must be passed to the kernel as raw pointers. SYCL also has a special mechanism for passing kernel arguments from host to the device. In OpenCL kernel arguments are set by calling `clSetKernelArg` function for each kernel argument, meanwhile in SYCL all the kernel arguments are fields of "SYCL kernel function" which can be defined as a lambda function or a named function object and passed as an argument to SYCL function for invoking kernels (such as `parallel_for` or `single_task`). To facilitate the mapping of SYCL kernel data members to OpenCL kernel arguments and overcome OpenCL limitations we added the generation of an OpenCL kernel function inside the compiler. An OpenCL kernel function contains the body of the SYCL kernel function, receives OpenCL-like parameters and additionally does some manipulation to initialize SYCL kernel data members with these parameters. In some pseudo code the OpenCL kernel function can look like this: ``` // SYCL kernel is defined in SYCL headers: template <typename KernelName, typename KernelType/*, ...*/> __attribute__((sycl_kernel)) void sycl_kernel_function(KernelType KernelFuncObj) { // ... KernelFuncObj(); } // Generated OpenCL kernel function __kernel KernelName(global int* a) { KernelType KernelFuncObj; // Actually kernel function object declaration // doesn't have a name in AST. // Let the kernel function object have one captured field - accessor A. // We need to init it with global pointer from arguments: KernelFuncObj.A.__init(a); // Body of the SYCL kernel from SYCL headers: { KernelFuncObj(); } } ``` OpenCL kernel function is generated by the compiler inside the Sema using AST nodes. Reviewers: bader, Naghasan, ABataev, keryell Subscribers: agozillon, mgorny, yaxunl, jfb, ebevhan, Anastasia, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D71016
- Loading branch information
Showing
18 changed files
with
1,010 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
#pragma once | ||
|
||
inline namespace cl { | ||
namespace sycl { | ||
namespace access { | ||
|
||
enum class target { | ||
global_buffer = 2014, | ||
constant_buffer, | ||
local, | ||
image, | ||
host_buffer, | ||
host_image, | ||
image_array | ||
}; | ||
|
||
enum class mode { | ||
read = 1024, | ||
write, | ||
read_write, | ||
discard_write, | ||
discard_read_write, | ||
atomic | ||
}; | ||
|
||
enum class placeholder { | ||
false_t, | ||
true_t | ||
}; | ||
|
||
enum class address_space : int { | ||
private_space = 0, | ||
global_space, | ||
constant_space, | ||
local_space | ||
}; | ||
} // namespace access | ||
|
||
template <int dim> | ||
struct id { | ||
template <typename... T> | ||
id(T... args) {} // fake constructor | ||
private: | ||
// Some fake field added to see using of id arguments in the | ||
// kernel wrapper | ||
int Data; | ||
}; | ||
|
||
template <int dim> | ||
struct range { | ||
template <typename... T> | ||
range(T... args) {} // fake constructor | ||
private: | ||
// Some fake field added to see using of range arguments in the | ||
// kernel wrapper | ||
int Data; | ||
}; | ||
|
||
template <int dim> | ||
struct _ImplT { | ||
range<dim> AccessRange; | ||
range<dim> MemRange; | ||
id<dim> Offset; | ||
}; | ||
|
||
template <typename dataT, int dimensions, access::mode accessmode, | ||
access::target accessTarget = access::target::global_buffer, | ||
access::placeholder isPlaceholder = access::placeholder::false_t> | ||
class accessor { | ||
|
||
public: | ||
void use(void) const {} | ||
template <typename... T> | ||
void use(T... args) {} | ||
template <typename... T> | ||
void use(T... args) const {} | ||
_ImplT<dimensions> impl; | ||
|
||
private: | ||
void __init(__attribute__((opencl_global)) dataT *Ptr, | ||
range<dimensions> AccessRange, | ||
range<dimensions> MemRange, id<dimensions> Offset) {} | ||
}; | ||
|
||
} // namespace sycl | ||
} // namespace cl |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// RUN: %clang_cc1 -fsycl-is-device -triple spir64-unknown-unknown -disable-llvm-passes -emit-llvm %s -o - | FileCheck %s | ||
|
||
// This test checks that compiler generates correct kernel wrapper for basic | ||
// case. | ||
|
||
#include "Inputs/sycl.hpp" | ||
|
||
template <typename name, typename Func> | ||
__attribute__((sycl_kernel)) void kernel(Func kernelFunc) { | ||
kernelFunc(); | ||
} | ||
|
||
int main() { | ||
cl::sycl::accessor<int, 1, cl::sycl::access::mode::read_write> accessorA; | ||
kernel<class kernel_function>( | ||
[=]() { | ||
accessorA.use(); | ||
}); | ||
return 0; | ||
} | ||
|
||
// CHECK: define {{.*}}spir_kernel void @{{.*}}kernel_function | ||
// CHECK-SAME: i32 addrspace(1)* [[MEM_ARG:%[a-zA-Z0-9_]+]], | ||
// CHECK-SAME: %"struct.cl::sycl::range"* byval{{.*}}align 4 [[ACC_RANGE:%[a-zA-Z0-9_]+_1]], | ||
// CHECK-SAME: %"struct.cl::sycl::range"* byval{{.*}}align 4 [[MEM_RANGE:%[a-zA-Z0-9_]+_2]], | ||
// CHECK-SAME: %"struct.cl::sycl::id"* byval{{.*}}align 4 [[OFFSET:%[a-zA-Z0-9_]+]]) | ||
// Check alloca for pointer argument | ||
// CHECK: [[MEM_ARG]].addr = alloca i32 addrspace(1)* | ||
// CHECK: [[ANONALLOCA:%[0-9]+]] = alloca %class.anon | ||
// CHECK: [[ARANGEA:%agg.tmp.*]] = alloca %"struct.cl::sycl::range" | ||
// CHECK: [[MRANGEA:%agg.tmp.*]] = alloca %"struct.cl::sycl::range" | ||
// CHECK: [[OIDA:%agg.tmp.*]] = alloca %"struct.cl::sycl::id" | ||
// CHECK: [[ANON:%[0-9]+]] = addrspacecast %class.anon* [[ANONALLOCA]] to %class.anon addrspace(4)* | ||
// CHECK: [[ARANGET:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::range"* [[ARANGEA]] to %"struct.cl::sycl::range" addrspace(4)* | ||
// CHECK: [[MRANGET:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::range"* [[MRANGEA]] to %"struct.cl::sycl::range" addrspace(4)* | ||
// CHECK: [[OIDT:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::id"* [[OIDA]] to %"struct.cl::sycl::id" addrspace(4)* | ||
// | ||
// Check store of kernel pointer argument to alloca | ||
// CHECK: store i32 addrspace(1)* [[MEM_ARG]], i32 addrspace(1)* addrspace(4)* [[MEM_ARG]].addr.ascast, align 8 | ||
|
||
// Check for default constructor of accessor | ||
// CHECK: call spir_func {{.*}}accessor | ||
|
||
// Check accessor GEP | ||
// CHECK: [[ACCESSOR:%[a-zA-Z0-9_]+]] = getelementptr inbounds %class.anon, %class.anon addrspace(4)* [[ANON]], i32 0, i32 0 | ||
|
||
// Check load from kernel pointer argument alloca | ||
// CHECK: [[MEM_LOAD:%[a-zA-Z0-9_]+]] = load i32 addrspace(1)*, i32 addrspace(1)* addrspace(4)* [[MEM_ARG]].addr.ascast | ||
|
||
// Check accessor __init method call | ||
// CHECK: [[ARANGE:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::range" addrspace(4)* [[ARANGET]] to %"struct.cl::sycl::range"* | ||
// CHECK: [[MRANGE:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::range" addrspace(4)* [[MRANGET]] to %"struct.cl::sycl::range"* | ||
// CHECK: [[OID:%agg.tmp.*]] = addrspacecast %"struct.cl::sycl::id" addrspace(4)* [[OIDT]] to %"struct.cl::sycl::id"* | ||
// CHECK: call spir_func void @{{.*}}__init{{.*}}(%"class.cl::sycl::accessor" addrspace(4)* {{[^,]*}} [[ACCESSOR]], i32 addrspace(1)* [[MEM_LOAD]], %"struct.cl::sycl::range"* byval({{.*}}) align 4 [[ARANGE]], %"struct.cl::sycl::range"* byval({{.*}}) align 4 [[MRANGE]], %"struct.cl::sycl::id"* byval({{.*}}) align 4 [[OID]]) | ||
|
||
// Check lambda "()" operator call | ||
// CHECK: call spir_func void @{{.*}}(%class.anon addrspace(4)* {{[^,]*}}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// RUN: %clang_cc1 -triple spir64 -fsycl-is-device -S -emit-llvm %s -o - | FileCheck %s | ||
|
||
template <typename T> | ||
T bar(T arg); | ||
|
||
void foo() { | ||
int a = 1 + 1 + bar(1); | ||
} | ||
|
||
template <typename T> | ||
T bar(T arg) { | ||
return arg; | ||
} | ||
|
||
template <typename name, typename Func> | ||
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) { | ||
kernelFunc(); | ||
} | ||
|
||
// Make sure that definitions for the types not used in SYCL kernels are not | ||
// emitted | ||
// CHECK-NOT: %struct.A | ||
// CHECK-NOT: @a = {{.*}} %struct.A | ||
struct A { | ||
int x = 10; | ||
} a; | ||
|
||
int main() { | ||
a.x = 8; | ||
kernel_single_task<class test_kernel>([]() { foo(); }); | ||
return 0; | ||
} | ||
|
||
// baz is not called from the SYCL kernel, so it must not be emitted | ||
// CHECK-NOT: define {{.*}} @{{.*}}baz | ||
void baz() {} | ||
|
||
// CHECK-LABEL: define dso_local spir_kernel void @{{.*}}test_kernel | ||
// CHECK-LABEL: define internal spir_func void @_ZZ4mainENKUlvE_clEv(%class.anon addrspace(4)* {{.*}}%this) | ||
// CHECK-LABEL: define dso_local spir_func void @{{.*}}foo | ||
// CHECK-LABEL: define linkonce_odr spir_func i32 @{{.*}}bar |
Oops, something went wrong.