Skip to content

[SYCL] Allow SYCL_EXTERNAL for class member functions #1295

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -2097,8 +2097,8 @@ def SYCLDeviceIndirectlyCallableDocs : Documentation {
This attribute can only be applied to functions and indicates that the
function must be treated as a device function and must be emitted even if it has
no direct uses from other SYCL device functions. However, it cannot be applied
to functions marked as 'static', functions declared within an anonymous
namespace or class member functions.
to functions marked as 'static' and functions declared within an anonymous
namespace.

It also means that function should be available externally and
cannot be optimized out due to reachability analysis or by any other
Expand Down
3 changes: 1 addition & 2 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10694,8 +10694,7 @@ def warn_boolean_attribute_argument_is_not_valid: Warning<
InGroup<AdjustedAttributes>;
def err_sycl_attibute_cannot_be_applied_here
: Error<"%0 attribute cannot be applied to a "
"%select{static function or function in an anonymous namespace"
"|class member function}1">;
"static function or function in an anonymous namespace">;
def warn_sycl_attibute_function_raw_ptr
: Warning<"SYCL 1.2.1 specification does not allow %0 attribute applied "
"to a function with a raw pointer "
Expand Down
14 changes: 14 additions & 0 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10156,6 +10156,10 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
if (D->hasAttr<AliasAttr>() || D->hasAttr<UsedAttr>())
return true;

if (LangOpts.SYCLIsDevice && !D->hasAttr<OpenCLKernelAttr>() &&
!D->hasAttr<SYCLDeviceAttr>())
return false;

if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
// Forward declarations aren't required.
if (!FD->doesThisDeclarationHaveABody())
Expand All @@ -10178,6 +10182,16 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
}
}

// Methods explcitly marked with 'sycl_device' attribute (via SYCL_EXTERNAL)
// or `indirectly_callable' attribute must be emitted regardless of number
// of actual uses
if (LangOpts.SYCLIsDevice && isa<CXXMethodDecl>(D)) {
if (auto *A = D->getAttr<SYCLDeviceIndirectlyCallableAttr>())
return !A->isImplicit();
if (auto *A = D->getAttr<SYCLDeviceAttr>())
return !A->isImplicit();
}

GVALinkage Linkage = GetGVALinkageForFunction(FD);

// static, static inline, always_inline, and extern inline functions can
Expand Down
10 changes: 5 additions & 5 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2516,11 +2516,6 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
if (Global->hasAttr<IFuncAttr>())
return emitIFuncDefinition(GD);

if (LangOpts.SYCLIsDevice) {
if (!Global->hasAttr<SYCLDeviceAttr>())
return;
}

// If this is a cpu_dispatch multiversion function, emit the resolver.
if (Global->hasAttr<CPUDispatchAttr>())
return emitCPUDispatchDefinition(GD);
Expand Down Expand Up @@ -2565,6 +2560,11 @@ void CodeGenModule::EmitGlobal(GlobalDecl GD) {
}
}

if (LangOpts.SYCLIsDevice && 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
18 changes: 2 additions & 16 deletions clang/lib/Sema/SemaDeclAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4428,13 +4428,7 @@ static void handleOptimizeNoneAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
static void handleSYCLDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
auto *FD = cast<FunctionDecl>(D);
if (!FD->isExternallyVisible()) {
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
<< AL << 0 /* static function or anonymous namespace */;
return;
}
if (isa<CXXMethodDecl>(FD)) {
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
<< AL << 1 /* class member function */;
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here) << AL;
return;
}
if (FD->getReturnType()->isPointerType()) {
Expand All @@ -4447,25 +4441,17 @@ static void handleSYCLDeviceAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
<< AL << 1 /* function with a raw pointer parameter type */;
}

S.addSyclDeviceDecl(D);
handleSimpleAttribute<SYCLDeviceAttr>(S, D, AL);
}

static void handleSYCLDeviceIndirectlyCallableAttr(Sema &S, Decl *D,
const ParsedAttr &AL) {
auto *FD = cast<FunctionDecl>(D);
if (!FD->isExternallyVisible()) {
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
<< AL << 0 /* static function or anonymous namespace */;
return;
}
if (isa<CXXMethodDecl>(FD)) {
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here)
<< AL << 1 /* class member function */;
S.Diag(AL.getLoc(), diag::err_sycl_attibute_cannot_be_applied_here) << AL;
return;
}

S.addSyclDeviceDecl(D);
D->addAttr(SYCLDeviceAttr::CreateImplicit(S.Context));
handleSimpleAttribute<SYCLDeviceIndirectlyCallableAttr>(S, D, AL);
}
Expand Down
59 changes: 1 addition & 58 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {

CheckSYCLType(Callee->getReturnType(), Callee->getSourceRange());

if (FunctionDecl *Def = Callee->getDefinition()) {
if (!Def->hasAttr<SYCLDeviceAttr>()) {
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
SemaRef.addSyclDeviceDecl(Def);
}
}
if (auto const *FD = dyn_cast<FunctionDecl>(Callee)) {
// FIXME: We need check all target specified attributes for error if
// that function with attribute can not be called from sycl kernel. The
Expand Down Expand Up @@ -265,23 +259,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
for (const auto &Arg : E->arguments())
CheckSYCLType(Arg->getType(), Arg->getSourceRange());

CXXConstructorDecl *Ctor = E->getConstructor();

if (FunctionDecl *Def = Ctor->getDefinition()) {
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
SemaRef.addSyclDeviceDecl(Def);
}

const auto *ConstructedType = Ctor->getParent();
if (ConstructedType->hasUserDeclaredDestructor()) {
CXXDestructorDecl *Dtor = ConstructedType->getDestructor();

if (FunctionDecl *Def = Dtor->getDefinition()) {
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
SemaRef.addSyclDeviceDecl(Def);
}
}
return true;
}

Expand Down Expand Up @@ -321,31 +298,6 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
return true;

CheckSYCLType(E->getType(), E->getSourceRange());
if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
if (!VD->isLocalVarDeclOrParm() && VD->hasGlobalStorage()) {
VD->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
SemaRef.addSyclDeviceDecl(VD);
}
}
return true;
}

bool VisitCXXNewExpr(CXXNewExpr *E) {
// Memory storage allocation is not allowed in kernels.
// All memory allocation for the device is done on
// the host using accessor classes. Consequently, the default
// allocation operator new overloads that allocate
// storage are disallowed in a SYCL kernel. The placement
// new operator and any user-defined overloads that
// do not allocate storage are permitted.
if (FunctionDecl *FD = E->getOperatorNew()) {
if (FunctionDecl *Def = FD->getDefinition()) {
if (!Def->hasAttr<SYCLDeviceAttr>()) {
Def->addAttr(SYCLDeviceAttr::CreateImplicit(SemaRef.Context));
SemaRef.addSyclDeviceDecl(Def);
}
}
}
return true;
}

Expand Down Expand Up @@ -1389,13 +1341,8 @@ void Sema::MarkDevice(void) {
}
}
for (const auto &elt : Marker.KernelSet) {
if (FunctionDecl *Def = elt->getDefinition()) {
if (!Def->hasAttr<SYCLDeviceAttr>()) {
Def->addAttr(SYCLDeviceAttr::CreateImplicit(Context));
addSyclDeviceDecl(Def);
}
if (FunctionDecl *Def = elt->getDefinition())
Marker.TraverseStmt(Def->getBody());
}
}
}

Expand Down Expand Up @@ -1467,10 +1414,6 @@ static void emitCallToUndefinedFnDiag(Sema &SemaRef, const FunctionDecl *Callee,
if (Callee->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate)
return;

// Don't emit diagnostic for functions not called from device code
if (!Caller->hasAttr<SYCLDeviceAttr>() && !Caller->hasAttr<SYCLKernelAttr>())
return;

bool RedeclHasAttr = false;

for (const Decl *Redecl : Callee->redecls()) {
Expand Down
61 changes: 57 additions & 4 deletions clang/test/CodeGenSYCL/device-indirectly-callable-attr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,63 @@ void foo() {
helper();
}

// CHECK: define spir_func void @{{.*foo.*}}() #[[ATTRS_FOO:[0-9]+]]
// CHECK: define spir_func void @{{.*foo.*}}() #[[ATTRS_INDIR_CALL:[0-9]+]]
// CHECK: call spir_func void @{{.*helper.*}}()
//
// CHECK: define spir_func void @{{.*helper.*}}() #[[ATTRS_HELPER:[0-9]+]]
// CHECK: define spir_func void @{{.*helper.*}}() #[[ATTRS_NOT_INDIR_CALL:[0-9]+]]
//
// CHECK: attributes #[[ATTRS_FOO]] = { {{.*}} "referenced-indirectly"
// CHECK-NOT: attributes #[[ATTRS_HELPER]] = { {{.*}} "referenced-indirectly"

int bar20(int a) { return a + 20; }

class A {
public:
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A3fooEv{{.*}}#[[ATTRS_INDIR_CALL]]
// CHECK-DAG: define spir_func i32 @_Z5bar20{{.*}}#[[ATTRS_NOT_INDIR_CALL]]
[[intel::device_indirectly_callable]] void foo() { bar20(10); }

// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AC1Ev{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] A() {}
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AD1Ev{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] ~A() {}

template <typename T>
[[intel::device_indirectly_callable]] void AFoo(T t) {}

// Templates are emitted when they are instantiated
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A4AFooIiEEvT_{{.*}}#[[ATTRS_INDIR_CALL]]
template <>
[[intel::device_indirectly_callable]] void AFoo<int>(int t) {}
};

struct Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN4Base12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] virtual void BaseWithAttr() { int a = 10; }
virtual void BaseWithoutAttr() { int b = 20; }
};

struct Overrider : Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] void BaseWithAttr() override { int a = 20; }
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider15BaseWithoutAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] void BaseWithoutAttr() override { int b = 30; }
};

struct Overrider1 : Base {
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Overrider112BaseWithAttrEv
void BaseWithAttr() override { int a = 20; }
};

struct Finalizer : Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer12BaseWithAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] void BaseWithAttr() final { int a = 20; }
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer15BaseWithoutAttrEv{{.*}}#[[ATTRS_INDIR_CALL]]
[[intel::device_indirectly_callable]] void BaseWithoutAttr() final { int b = 30; }
};

struct Finalizer1 : Base {
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Finalizer112BaseWithAttrEv
void BaseWithAttr() final { int a = 20; }
};

// CHECK: attributes #[[ATTRS_INDIR_CALL]] = { {{.*}} "referenced-indirectly"
// CHECK-NOT: attributes #[[ATTRS_NOT_INDIR_CALL]] = { {{.*}} "referenced-indirectly"
78 changes: 78 additions & 0 deletions clang/test/CodeGenSYCL/sycl-device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,84 @@

int bar(int b);

int bar10(int a) { return a + 10; }
int bar20(int a) { return a + 20; }

class A {
public:
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A3fooEv
// CHECK-DAG: define spir_func i32 @_Z5bar20i
__attribute__((sycl_device)) void foo() { bar20(10); }

// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AC1Ev
// CHECK-DAG: define spir_func i32 @_Z5bar10i
__attribute__((sycl_device))
A() { bar10(10); }
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1AD1Ev
__attribute__((sycl_device)) ~A() {}

template <typename T>
__attribute__((sycl_device)) void AFoo(T t) {}

// Templates are emitted when they are instantiated
// CHECK-DAG: define linkonce_odr spir_func void @_ZN1A4AFooIiEEvT_
template <>
__attribute__((sycl_device)) void AFoo<int>(int t) {}

// CHECK-DAG: define linkonce_odr spir_func i32 @_ZN1A13non_annotatedEv
int non_annotated() { return 1; }

// CHECK-DAG: define linkonce_odr spir_func i32 @_ZN1A9annotatedEv
__attribute__((sycl_device)) int annotated() { return non_annotated() + 1; }
};

template <typename T>
struct B {
T data;
B(T _data) : data(_data) {}

__attribute__((sycl_device)) void BFoo(T t) {}
};

template <>
struct B<int> {
int data;
B(int _data) : data(_data) {}

// CHECK-DAG: define linkonce_odr spir_func void @_ZN1BIiE4BFooEi
__attribute__((sycl_device)) void BFoo(int t) {}
};

struct Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN4Base12BaseWithAttrEv
__attribute__((sycl_device)) virtual void BaseWithAttr() { int a = 10; }
virtual void BaseWithoutAttr() { int b = 20; }
};

struct Overrider : Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider12BaseWithAttrEv
__attribute__((sycl_device)) void BaseWithAttr() override { int a = 20; }
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Overrider15BaseWithoutAttrEv
__attribute__((sycl_device)) void BaseWithoutAttr() override { int b = 30; }
};

struct Overrider1 : Base {
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Overrider112BaseWithAttrEv
void BaseWithAttr() override { int a = 20; }
};

struct Finalizer : Base {
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer12BaseWithAttrEv
__attribute__((sycl_device)) void BaseWithAttr() final { int a = 20; }
// CHECK-DAG: define linkonce_odr spir_func void @_ZN9Finalizer15BaseWithoutAttrEv
__attribute__((sycl_device)) void BaseWithoutAttr() final { int b = 30; }
};

struct Finalizer1 : Base {
// CHECK-NOT: define linkonce_odr spir_func void @_ZN10Finalizer112BaseWithAttrEv
void BaseWithAttr() final { int a = 20; }
};

// CHECK-DAG: define spir_func i32 @_Z3fooii
__attribute__((sycl_device))
int foo(int a, int b) { return a + bar(b); }
Expand Down
3 changes: 3 additions & 0 deletions clang/test/SemaSYCL/call-to-undefined-function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ void forwardDeclFn() {
}

int main() {
// No problems in host code
undefined();

kernel_single_task<class CallToUndefinedFnTester>([]() {
// expected-note@-1 {{called by 'operator()'}}
// expected-note@-2 {{called by 'operator()'}}
Expand Down
Loading