Skip to content

Commit

Permalink
[SYCL] Implement OpenCL kernel function generation
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Fznamznon committed Dec 4, 2019
1 parent 28e4942 commit 02e8944
Show file tree
Hide file tree
Showing 16 changed files with 1,035 additions and 3 deletions.
13 changes: 13 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11632,6 +11632,19 @@ class Sema final {
ConstructorDestructor,
BuiltinFunction
};

private:
/// Contains generated OpenCL kernel functions for SYCL.
SmallVector<Decl *, 4> SYCLKernels;

public:
void addSYCLKernel(Decl *D) { SYCLKernels.push_back(D); }
/// Access to SYCL kernels.
SmallVectorImpl<Decl *> &getSYCLKernels() { return SYCLKernels; }

/// Constructs an OpenCL kernel using the KernelCaller function and adds it to
/// the SYCL device code.
void constructOpenCLKernel(FunctionDecl *KernelCallerFunc, MangleContext &MC);
};

/// RAII object that enters a new expression evaluation context.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10023,6 +10023,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
return true;

// If SYCL, only kernels are required.
if (LangOpts.SYCLIsDevice && !(D->hasAttr<OpenCLKernelAttr>()))
return false;

if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Forward declarations aren't required.
if (!FD->doesThisDeclarationHaveABody())
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2474,6 +2474,12 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
}
}

if (LangOpts.SYCLIsDevice && Global->hasAttr<OpenCLKernelAttr>() &&
MustBeEmitted(Global)) {
addDeferredDeclToEmit(GD);
return;
}

// Ignore declarations, they will be emitted on their first use.
if (const auto *FD = dyn_cast<FunctionDecl>(Global)) {
// Forward declarations are emitted lazily on first use.
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Parse/ParseAST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) {
for (Decl *D : S.WeakTopLevelDecls())
Consumer->HandleTopLevelDecl(DeclGroupRef(D));

if (S.getLangOpts().SYCLIsDevice)
for (Decl *D : S.getSYCLKernels())
Consumer->HandleTopLevelDecl(DeclGroupRef(D));

Consumer->HandleTranslationUnit(S.getASTContext());

// Finalize the template instantiation observer chain.
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ add_clang_library(clangSema
SemaStmt.cpp
SemaStmtAsm.cpp
SemaStmtAttr.cpp
SemaSYCL.cpp
SemaTemplate.cpp
SemaTemplateDeduction.cpp
SemaTemplateInstantiate.cpp
Expand Down
Loading

0 comments on commit 02e8944

Please sign in to comment.