Skip to content

Commit

Permalink
[SYCL] Mark kernel on host for enabling optimizations. (#2186)
Browse files Browse the repository at this point in the history
To enable better optimizations on the host, kernel implementation
routines may be marked with the attribute [[clang::sycl_kernel_impl]].
The function object passed to the SYCL kernel is also marked with
alwaysinline on the host to enable these optimizations to be more
effective.

Signed-off-by: Premanand M Rao <premanand.m.rao@intel.com>
  • Loading branch information
premanandrao authored Aug 5, 2020
1 parent 0c38b35 commit 414c1e5
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 38 deletions.
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -1131,7 +1131,7 @@ def SYCLDevice : InheritableAttr {
def SYCLKernel : InheritableAttr {
let Spellings = [Clang<"sycl_kernel">];
let Subjects = SubjectList<[FunctionTmpl]>;
let LangOpts = [SYCLIsDevice];
let LangOpts = [SYCLIsHost, SYCLIsDevice];
let Documentation = [SYCLKernelDocs];
}

Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10919,8 +10919,8 @@ def warn_sycl_kernel_invalid_template_param_type : Warning<
"template parameter of a function template with the 'sycl_kernel' attribute"
" cannot be a non-type template parameter">, InGroup<IgnoredAttributes>;
def warn_sycl_kernel_num_of_function_params : Warning<
"function template with 'sycl_kernel' attribute must have a single parameter">,
InGroup<IgnoredAttributes>;
"function template with 'sycl_kernel' attribute must have at least one"
" parameter">, InGroup<IgnoredAttributes>;
def warn_sycl_kernel_return_type : Warning<
"function template with 'sycl_kernel' attribute must have a 'void' return type">,
InGroup<IgnoredAttributes>;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,9 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
if (D && D->hasAttr<CFICanonicalJumpTableAttr>())
Fn->addFnAttr("cfi-canonical-jump-table");

if (getLangOpts().SYCLIsHost && D && D->hasAttr<SYCLKernelAttr>())
Fn->addFnAttr("sycl_kernel");

if (getLangOpts().OpenCL || getLangOpts().SYCLIsDevice) {
// Add metadata for a kernel function.
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
Expand Down
13 changes: 7 additions & 6 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7188,15 +7188,16 @@ static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
const FunctionTemplateDecl *FT = FD->getDescribedFunctionTemplate();
assert(FT && "Function template is expected");

// Function template must have at least two template parameters.
// Function template must have at least two template parameters so it
// can be used in OpenCL kernel generation.
const TemplateParameterList *TL = FT->getTemplateParameters();
if (TL->size() < 2) {
if (S.LangOpts.SYCLIsDevice && TL->size() < 2) {
S.Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_template_params);
return;
}

// Template parameters must be typenames.
for (unsigned I = 0; I < 2; ++I) {
// The first two template parameters must be typenames.
for (unsigned I = 0; I < 2 && I < TL->size(); ++I) {
const NamedDecl *TParam = TL->getParam(I);
if (isa<NonTypeTemplateParmDecl>(TParam)) {
S.Diag(FT->getLocation(),
Expand All @@ -7205,8 +7206,8 @@ static void handleSYCLKernelAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
}
}

// Function must have at least one argument.
if (getFunctionOrMethodNumParams(D) != 1) {
// Function must have at least one parameter.
if (getFunctionOrMethodNumParams(D) < 1) {
S.Diag(FT->getLocation(), diag::warn_sycl_kernel_num_of_function_params);
return;
}
Expand Down
59 changes: 32 additions & 27 deletions clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6148,6 +6148,32 @@ NamedDecl *Sema::FindInstantiatedDecl(SourceLocation Loc, NamedDecl *D,
return D;
}

static void processSYCLKernel(Sema &S, FunctionDecl *FD, MangleContext &MC) {
if (S.LangOpts.SYCLIsDevice) {
S.ConstructOpenCLKernel(FD, MC);
} else if (S.LangOpts.SYCLIsHost) {
CXXRecordDecl *CRD = (*FD->param_begin())->getType()->getAsCXXRecordDecl();
for (auto *Method : CRD->methods())
if (Method->getOverloadedOperator() == OO_Call &&
!Method->hasAttr<AlwaysInlineAttr>())
Method->addAttr(AlwaysInlineAttr::CreateImplicit(S.getASTContext()));
}
}

static void processFunctionInstantiation(Sema &S,
SourceLocation PointOfInstantiation,
FunctionDecl *FD,
bool DefinitionRequired,
MangleContext &MC) {
S.InstantiateFunctionDefinition(/*FIXME:*/ PointOfInstantiation, FD, true,
DefinitionRequired, true);
if (!FD->isDefined())
return;
if (FD->hasAttr<SYCLKernelAttr>())
processSYCLKernel(S, FD, MC);
FD->setInstantiationIsPending(false);
}

/// Performs template instantiation for all implicit template
/// instantiations we have seen until this point.
void Sema::PerformPendingInstantiations(bool LocalOnly) {
Expand All @@ -6170,37 +6196,16 @@ void Sema::PerformPendingInstantiations(bool LocalOnly) {
if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first)) {
bool DefinitionRequired = Function->getTemplateSpecializationKind() ==
TSK_ExplicitInstantiationDefinition;
if (Function->isMultiVersion()) {
if (Function->isMultiVersion())
getASTContext().forEachMultiversionedFunctionVersion(
Function, [this, Inst, DefinitionRequired,
MangleCtx = move(MangleCtx)](FunctionDecl *CurFD) {
InstantiateFunctionDefinition(/*FIXME:*/ Inst.second, CurFD, true,
DefinitionRequired, true);
if (CurFD->isDefined()) {
// Because all SYCL kernel functions are template functions - they
// have deferred instantination. We need bodies of these functions
// so we are checking for SYCL kernel attribute after instantination.
if (getLangOpts().SYCLIsDevice &&
CurFD->hasAttr<SYCLKernelAttr>()) {
ConstructOpenCLKernel(CurFD, *MangleCtx);
}
CurFD->setInstantiationIsPending(false);
}
processFunctionInstantiation(*this, Inst.second, CurFD,
DefinitionRequired, *MangleCtx);
});
} else {
InstantiateFunctionDefinition(/*FIXME:*/ Inst.second, Function, true,
DefinitionRequired, true);
if (Function->isDefined()) {
// Because all SYCL kernel functions are template functions - they
// have deferred instantination. We need bodies of these functions
// so we are checking for SYCL kernel attribute after instantination.
if (getLangOpts().SYCLIsDevice &&
Function->hasAttr<SYCLKernelAttr>()) {
ConstructOpenCLKernel(Function, *MangleCtx);
}
Function->setInstantiationIsPending(false);
}
}
else
processFunctionInstantiation(*this, Inst.second, Function,
DefinitionRequired, *MangleCtx);
// Definition of a PCH-ed template declaration may be available only in the TU.
if (!LocalOnly && LangOpts.PCHInstantiateTemplates &&
TUKind == TU_Prefix && Function->instantiationIsPending())
Expand Down
44 changes: 44 additions & 0 deletions clang/test/CodeGenSYCL/sycl_kernel-host.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// RUN: %clang_cc1 -fsycl -fsycl-is-host -triple spir64 -disable-llvm-passes %s -emit-llvm -o - | FileCheck %s
// Test that the kernel implementation routine marked with 'sycl_kernel'
// has the attribute 'sycl_kernel' in the generated LLVM IR and that the
// function object passed to the sycl kernel is marked 'alwaysinline'
// on the host.

// CHECK: define spir_func void @{{.*}}func{{.*}}() #[[NOSKA:[0-9]+]] {
// CHECK: define internal spir_func void @{{.*}}Kernel{{.*}}Foo{{.*}}({{.*}}) #[[SKA:[0-9]+]] {
// CHECK: call spir_func void @{{.*}}KernelImpl{{.*}}({{.*}}, i32 1, double 2.000000e+00)
// CHECK: define internal spir_func void @{{.*}}Kernel{{.*}}Bar{{.*}}({{.*}}) #[[SKA]] {
// CHECK: call spir_func void @{{.*}}KernelImpl{{.*}}({{.*}}, i32 1, double 2.000000e+00)
// CHECK: define internal spir_func void @{{.*}}KernelImpl{{.*}}({{.*}} %f, i32 %i, double %d) #[[SKA]] {
// CHECK: call spir_func void @"{{.*}}func{{.*}}"(%class
// CHECK: define internal spir_func void @{{.*}}func{{.*}}(%class.anon* %this, i32 %i, double %d) #[[ALWAYSINLINE:[0-9]+]]
// CHECK: define linkonce_odr spir_func void @{{.*}}KernelImpl{{.*}}Functor{{.*}}({{.*}}, i32 %i, double %d) #[[SKA]] comdat {
// CHECK: call spir_func void @{{.*}}Functor{{.*}}(%struct
// CHECK: define linkonce_odr spir_func void @{{.*}}Functor{{.*}}(%struct.Functor* %this, i32 %i, double %d) #[[ALWAYSINLINE]]

template <typename Func>
void __attribute__((sycl_kernel))
KernelImpl(Func f, int i, double d) {
// CHECK-NOT: call void
f(i, d);
}

template <typename Name, typename Func>
void __attribute__((sycl_kernel))
Kernel(Func f) {
KernelImpl(f, 1, 2.0);
}

struct Functor {
void operator()(int i, double d) { d = i + 2; };
} functionobj;

void func() {
auto Lambda = [](int i, double d) { d += i; };
Kernel<class Foo>(Lambda);
Kernel<class Bar>(functionobj);
}

// CHECK-NOT: attributes #[[NOSKA]] = { {{.*}}"sycl_kernel"{{.*}} }
// CHECK: attributes #[[SKA]] = { {{.*}}"sycl_kernel"{{.*}} }
// CHECK: attributes #[[ALWAYSINLINE]] = { {{.*}}alwaysinline{{.*}} }
27 changes: 25 additions & 2 deletions clang/test/SemaSYCL/kernel-attribute.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -fsycl -fsycl-is-device -verify %s
// RUN: %clang_cc1 -fsycl -fsycl-is-host -DHOST -fsyntax-only -verify %s

// Only function templates
[[clang::sycl_kernel]] int gv2 = 0; // expected-warning {{'sycl_kernel' attribute only applies to function templates}}
Expand All @@ -13,11 +14,13 @@ __attribute__((sycl_kernel(1))) void foo(T P); // expected-error {{'sycl_kernel'
template <typename T, typename A, int I>
[[clang::sycl_kernel(1)]] void foo1(T P);// expected-error {{'sycl_kernel' attribute takes no arguments}}

#ifndef HOST
// At least two template parameters
template <typename T>
__attribute__((sycl_kernel)) void foo(T P); // expected-warning {{'sycl_kernel' attribute only applies to a function template with at least two template parameters}}
template <typename T>
[[clang::sycl_kernel]] void foo1(T P); // expected-warning {{'sycl_kernel' attribute only applies to a function template with at least two template parameters}}
#endif

// First two template parameters cannot be non-type template parameters
template <typename T, int A>
Expand All @@ -33,12 +36,32 @@ template <typename T, typename A>

// Must take at least one argument
template <typename T, typename A>
__attribute__((sycl_kernel)) void foo(); // expected-warning {{function template with 'sycl_kernel' attribute must have a single parameter}}
__attribute__((sycl_kernel)) void foo(); // expected-warning {{function template with 'sycl_kernel' attribute must have at least one parameter}}
template <typename T, typename A>
[[clang::sycl_kernel]] void foo1(T t, A a); // expected-warning {{function template with 'sycl_kernel' attribute must have a single parameter}}
[[clang::sycl_kernel]] void foo1(T t, A a); // no diagnostics

// No diagnostics
template <typename T, typename A>
__attribute__((sycl_kernel)) void foo(T P);
template <typename T, typename A, int I>
[[clang::sycl_kernel]] void foo1(T P);

#ifdef HOST
// No diagnostics
template <typename Func>
void __attribute__((sycl_kernel))
KernelImpl4(Func f, int i, double d) {
f(i, d);
}

template <typename Name, typename Func>
void __attribute__((sycl_kernel))
Kernel(Func f) {
KernelImpl4(f, 1, 2.0);
}

void func() {
auto Lambda = [](int i, double d) { d += i; };
Kernel<class Foo>(Lambda);
}
#endif

0 comments on commit 414c1e5

Please sign in to comment.