Skip to content

[SYCL] Forbid non-const static variable uses in device code #1213

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 7 commits into from
Feb 28, 2020
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -10626,7 +10626,7 @@ def err_sycl_kernel_name_class_not_top_level : Error<
"nest in a namespace: %0">;
def err_sycl_restrict : Error<
"SYCL kernel cannot "
"%select{use a global variable"
"%select{use a non-const global variable"
"|use rtti"
"|use a non-const static data variable"
"|call a virtual function"
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,15 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs,
bool ObjCPropertyAccess,
bool AvoidPartialAvailabilityChecks,
ObjCInterfaceDecl *ClassReceiver) {
if (getLangOpts().SYCLIsDevice) {
if (auto VD = dyn_cast<VarDecl>(D)) {
if (VD->getStorageClass() == SC_Static &&
!VD->getType().isConstant(Context))
SYCLDiagIfDeviceCode(*Locs.begin(), diag::err_sycl_restrict)
<< Sema::KernelNonConstStaticDataVariable;
}
}

SourceLocation Loc = Locs.front();
if (getLangOpts().CPlusPlus && isa<FunctionDecl>(D)) {
// If there were any diagnostics suppressed by template argument deduction,
Expand Down
19 changes: 3 additions & 16 deletions clang/lib/Sema/SemaSYCL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,29 +321,16 @@ class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
return true;
}

bool VisitMemberExpr(MemberExpr *E) {
if (VarDecl *VD = dyn_cast<VarDecl>(E->getMemberDecl())) {
bool IsConst = VD->getType().getNonReferenceType().isConstQualified();
if (!IsConst && VD->isStaticDataMember())
SemaRef.Diag(E->getExprLoc(), diag::err_sycl_restrict)
<< Sema::KernelNonConstStaticDataVariable;
}
return true;
}

bool VisitDeclRefExpr(DeclRefExpr *E) {
Decl* D = E->getDecl();
Decl *D = E->getDecl();
if (SemaRef.isKnownGoodSYCLDecl(D))
return true;

CheckSYCLType(E->getType(), E->getSourceRange());
if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
bool IsConst = VD->getType().getNonReferenceType().isConstQualified();
if (!IsConst && VD->isStaticDataMember())
SemaRef.Diag(E->getExprLoc(), diag::err_sycl_restrict)
<< Sema::KernelNonConstStaticDataVariable;
else if (!IsConst && VD->hasGlobalStorage() && !VD->isStaticLocal() &&
!VD->isStaticDataMember() && !isa<ParmVarDecl>(VD)) {
if (!IsConst && VD->hasGlobalStorage() && !VD->isStaticLocal() &&
!VD->isStaticDataMember() && !isa<ParmVarDecl>(VD)) {
if (VD->getTLSKind() != VarDecl::TLS_None)
SemaRef.Diag(E->getLocation(), diag::err_thread_unsupported);
SemaRef.Diag(E->getLocation(), diag::err_sycl_restrict)
Expand Down
22 changes: 22 additions & 0 deletions clang/test/SemaSYCL/sycl-device-static-restrict.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// RUN: %clang_cc1 -verify -fsyntax-only -fsycl-is-device %s
const int glob1 = 1;
int glob2 = 2;
template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
// expected-note-re@+1{{called by 'kernel_single_task<fake_kernel, (lambda at {{.*}})>}}
kernelFunc();
}

int main() {
static int n = 0;
const static int l = 0;
kernel_single_task<class fake_kernel>([]() {
int m = l;
m = glob1;
// expected-error@+1{{SYCL kernel cannot use a non-const static data variable}}
m = n;
// expected-error@+1{{SYCL kernel cannot use a non-const global variable}}
m = glob2;
});
return 0;
}
130 changes: 67 additions & 63 deletions clang/test/SemaSYCL/sycl-restrict.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,39 @@
// RUN: %clang_cc1 -fcxx-exceptions -triple spir64 -fsycl-is-device -fno-sycl-allow-func-ptr -Wno-return-type -verify -fsyntax-only -std=c++17 %s
// RUN: %clang_cc1 -fcxx-exceptions -triple spir64 -fsycl-is-device -DALLOW_FP=1 -fsycl-allow-func-ptr -Wno-return-type -verify -fsyntax-only -std=c++17 %s


namespace std {
class type_info;
typedef __typeof__(sizeof(int)) size_t;
}
class type_info;
typedef __typeof__(sizeof(int)) size_t;
} // namespace std
namespace Check_User_Operators {
class Fraction
{
// expected-error@+2 {{SYCL kernel cannot call a recursive function}}
// expected-note@+1 {{function implemented using recursion declared here}}
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int n, d;
class Fraction {
// expected-error@+2 {{SYCL kernel cannot call a recursive function}}
// expected-note@+1 {{function implemented using recursion declared here}}
int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); }
int n, d;

public:
Fraction(int n, int d = 1) : n(n/gcd(n, d)), d(d/gcd(n, d)) { }
int num() const { return n; }
int den() const { return d; }
Fraction(int n, int d = 1) : n(n / gcd(n, d)), d(d / gcd(n, d)) {}
int num() const { return n; }
int den() const { return d; }
};
bool operator==(const Fraction& lhs, const Fraction& rhs)
{
new int; // expected-error {{SYCL kernel cannot allocate storage}}
return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}}
bool operator==(const Fraction &lhs, const Fraction &rhs) {
new int; // expected-error {{SYCL kernel cannot allocate storage}}
return lhs.num() == rhs.num() && lhs.den() == rhs.den();
}
} // namespace Check_User_Operators

namespace Check_VLA_Restriction {
void no_restriction(int p) {
int index[p+2];
int index[p + 2];
}
void restriction(int p) {
// expected-error@+1 {{variable length arrays are not supported for the current target}}
int index[p+2];
}
int index[p + 2];
}
} // namespace Check_VLA_Restriction

void* operator new (std::size_t size, void* ptr) throw() { return ptr; };
void *operator new(std::size_t size, void *ptr) throw() { return ptr; };
namespace Check_RTTI_Restriction {
struct A {
virtual ~A(){};
Expand All @@ -50,35 +49,38 @@ struct OverloadedNewDelete {
void *operator new(std::size_t size) throw() {
// expected-error@+1 {{SYCL kernel cannot allocate storage}}
float *pt = new float;
return 0;}
return 0;
}
// This overload does not allocate: no diagnostic.
void *operator new[](std::size_t size) throw() {return 0;}
void *operator new[](std::size_t size) throw() { return 0; }
void operator delete(void *){};
void operator delete[](void *){};
};

bool isa_B(A *a) {
Check_User_Operators::Fraction f1(3, 8), f2(1, 2), f3(10, 2);
if (f1 == f2) return false;
if (f1 == f2)
return false;

Check_VLA_Restriction::restriction(7);
// expected-error@+1 {{SYCL kernel cannot allocate storage}}
int *ip = new int;
int i; int *p3 = new(&i) int; // no error on placement new
int i;
int *p3 = new (&i) int; // no error on placement new
// expected-note@+1 {{called by 'isa_B'}}
OverloadedNewDelete *x = new( struct OverloadedNewDelete );
auto y = new struct OverloadedNewDelete [5];
OverloadedNewDelete *x = new (struct OverloadedNewDelete);
auto y = new struct OverloadedNewDelete[5];
// expected-error@+1 {{SYCL kernel cannot use rtti}}
(void)typeid(int);
// expected-error@+1 {{SYCL kernel cannot use rtti}}
return dynamic_cast<B *>(a) != 0;
}

template<typename N, typename L>
template <typename N, typename L>
__attribute__((sycl_kernel)) void kernel1(L l) {
l();
}
}
} // namespace Check_RTTI_Restriction

typedef struct Base {
virtual void f() const {}
Expand All @@ -87,22 +89,19 @@ typedef struct Base {
typedef struct A {
static int stat_member;
const static int const_stat_member;
constexpr static int constexpr_stat_member=0;
constexpr static int constexpr_stat_member = 0;

int fm(void)
{
int fm(void) {
// expected-error@+1 {{SYCL kernel cannot use a non-const static data variable}}
return stat_member;
}
} a_type;


b_type b;

using myFuncDef = int(int,int);
using myFuncDef = int(int, int);

void eh_ok(void)
{
void eh_ok(void) {
__float128 A;
try {
;
Expand All @@ -112,8 +111,7 @@ void eh_ok(void)
throw 20;
}

void eh_not_ok(void)
{
void eh_not_ok(void) {
// expected-error@+1 {{SYCL kernel cannot use exceptions}}
try {
;
Expand All @@ -134,7 +132,7 @@ void usage(myFuncDef functionPtr) {
// expected-error@+2 {{SYCL kernel cannot call through a function pointer}}
#endif
if ((*functionPtr)(1, 2))
// expected-error@+2 {{SYCL kernel cannot use a global variable}}
// expected-error@+2 {{SYCL kernel cannot use a non-const global variable}}
// expected-error@+1 {{SYCL kernel cannot call a virtual function}}
b.f();
Check_RTTI_Restriction::kernel1<class kernel_name>([]() {
Expand All @@ -146,58 +144,64 @@ void usage(myFuncDef functionPtr) {
}

namespace ns {
int glob;
int glob;
}
extern "C++" {
int another_global = 5;
namespace AnotherNS {
int moar_globals = 5;
}
int another_global = 5;
namespace AnotherNS {
int moar_globals = 5;
}
}

int addInt(int n, int m) {
return n+m;
return n + m;
}

int use2 ( a_type ab, a_type *abp ) {
int use2(a_type ab, a_type *abp) {

if (ab.constexpr_stat_member) return 2;
if (ab.const_stat_member) return 1;
if (ab.constexpr_stat_member)
return 2;
if (ab.const_stat_member)
return 1;
// expected-error@+1 {{SYCL kernel cannot use a non-const static data variable}}
if (ab.stat_member) return 0;
if (ab.stat_member)
return 0;
// expected-error@+1 {{SYCL kernel cannot use a non-const static data variable}}
if (abp->stat_member) return 0;
if (ab.fm()) return 0;
// expected-error@+1 {{SYCL kernel cannot use a global variable}}
return another_global ;
// expected-error@+1 {{SYCL kernel cannot use a global variable}}
if (abp->stat_member)
return 0;
// expected-note@+1 {{called by 'use2'}}
if (ab.fm())
return 0;
// expected-error@+1 {{SYCL kernel cannot use a non-const global variable}}
return another_global;
// expected-error@+1 {{SYCL kernel cannot use a non-const global variable}}
return ns::glob +
// expected-error@+1 {{SYCL kernel cannot use a global variable}}
AnotherNS::moar_globals;
// expected-error@+1 {{SYCL kernel cannot use a non-const global variable}}
AnotherNS::moar_globals;
// expected-note@+1 {{called by 'use2'}}
eh_not_ok();
Check_RTTI_Restriction:: A *a;
Check_RTTI_Restriction::A *a;
// expected-note@+1 2{{called by 'use2'}}
Check_RTTI_Restriction:: isa_B(a);
Check_RTTI_Restriction::isa_B(a);
// expected-note@+1 {{called by 'use2'}}
usage(&addInt);
Check_User_Operators::Fraction f1(3, 8), f2(1, 2), f3(10, 2);
// expected-note@+1 {{called by 'use2'}}
if (f1 == f2) return false;
if (f1 == f2)
return false;
}

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
kernelFunc();
a_type ab;
a_type *p;
// expected-note@+1 5{{called by 'kernel_single_task}}
// expected-note@+1 7{{called by 'kernel_single_task}}
use2(ab, p);
}

int main() {
a_type ab;
kernel_single_task<class fake_kernel>([]() { usage( &addInt ); });
kernel_single_task<class fake_kernel>([]() { usage(&addInt); });
return 0;
}

4 changes: 2 additions & 2 deletions clang/test/SemaSYCL/tls_error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ extern __thread void (*__once_call)(); // expected-no-error

void usage() {
// expected-error@+2{{thread-local storage is not supported for the current target}}
// expected-error@+1{{SYCL kernel cannot use a global variable}}
// expected-error@+1{{SYCL kernel cannot use a non-const global variable}}
__once_callable = 0;
// expected-error@+3{{thread-local storage is not supported for the current target}}
// expected-error@+2{{SYCL kernel cannot use a global variable}}
// expected-error@+2{{SYCL kernel cannot use a non-const global variable}}
// expected-error@+1{{SYCL kernel cannot call through a function pointer}}
__once_call();
}
Expand Down