Skip to content

Commit df77c7e

Browse files
Fznamznonbader
authored andcommitted
[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
1 parent 0668e3d commit df77c7e

18 files changed

+904
-7
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12401,6 +12401,7 @@ class Sema final {
1240112401
ConstructorDestructor,
1240212402
BuiltinFunction
1240312403
};
12404+
1240412405
/// Creates a DeviceDiagBuilder that emits the diagnostic if the current
1240512406
/// context is "used as device code".
1240612407
///
@@ -12435,6 +12436,19 @@ class Sema final {
1243512436
/// Adds Callee to DeviceCallGraph if we don't know if its caller will be
1243612437
/// codegen'ed yet.
1243712438
bool checkSYCLDeviceFunction(SourceLocation Loc, FunctionDecl *Callee);
12439+
12440+
private:
12441+
/// Contains generated OpenCL kernel functions for SYCL.
12442+
SmallVector<Decl *, 4> SYCLKernels;
12443+
12444+
public:
12445+
void addSYCLKernel(Decl *D) { SYCLKernels.push_back(D); }
12446+
/// Access to SYCL kernels.
12447+
SmallVectorImpl<Decl *> &getSYCLKernels() { return SYCLKernels; }
12448+
12449+
/// Constructs an OpenCL kernel using the KernelCaller function and adds it to
12450+
/// the SYCL device code.
12451+
void constructOpenCLKernel(FunctionDecl *KernelCallerFunc, MangleContext &MC);
1243812452
};
1243912453

1244012454
/// RAII object that enters a new expression evaluation context.

clang/lib/AST/ASTContext.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10446,6 +10446,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1044610446
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
1044710447
return true;
1044810448

10449+
// If SYCL, only kernels are required.
10450+
if (LangOpts.SYCLIsDevice && !(D->hasAttr<OpenCLKernelAttr>()))
10451+
return false;
10452+
1044910453
if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
1045010454
// Forward declarations aren't required.
1045110455
if (!FD->doesThisDeclarationHaveABody())

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2590,6 +2590,12 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
25902590
}
25912591
}
25922592

2593+
if (LangOpts.SYCLIsDevice && Global->hasAttr<OpenCLKernelAttr>() &&
2594+
MustBeEmitted(Global)) {
2595+
addDeferredDeclToEmit(GD);
2596+
return;
2597+
}
2598+
25932599
// Ignore declarations, they will be emitted on their first use.
25942600
if (const auto *FD = dyn_cast<FunctionDecl>(Global)) {
25952601
// Forward declarations are emitted lazily on first use.

clang/lib/Parse/ParseAST.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,10 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
168168
for (Decl *D : S.WeakTopLevelDecls())
169169
Consumer->HandleTopLevelDecl(DeclGroupRef(D));
170170

171+
if (S.getLangOpts().SYCLIsDevice)
172+
for (Decl *D : S.getSYCLKernels())
173+
Consumer->HandleTopLevelDecl(DeclGroupRef(D));
174+
171175
Consumer->HandleTranslationUnit(S.getASTContext());
172176

173177
// Finalize the template instantiation observer chain.

0 commit comments

Comments
 (0)