Skip to content

Commit 808c5c8

Browse files
[SYCL] Move type checks to later in Semantic Analysis lifecycle (#1465)
SPIR-V doesn't support certain types (quad type, __int128, zero length arrays and other). So we check any code that is running on the kernel for those types and emit an error if they are encountered. Presently, these checks are done in `ConvertDeclSpecToType` in SemaType.cpp, but this is too early in the Sema lifecycle. It can catch only the most straightforward type declarations. We need to also catch types that use `auto` declarations, `typedef`, templating and more. To do that, the type checking must occur a little later in the Semantic Analysis lifecycle. Here I am calling the checks from `CheckCompleteVariableDeclaration` in SemaDecl.cpp. This is called just after the parsing is finished and seems to work well. Also, these checks now avoid the problems we encountered with deferred diagnostics getting confused by templated functions. So, no further change is needed to the deferred diagnostic system (as far as these checks are concerned). Expanded the testing as well. Signed-off-by: Chris Perkins <[email protected]>
1 parent c30c80e commit 808c5c8

File tree

6 files changed

+348
-31
lines changed

6 files changed

+348
-31
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12455,6 +12455,7 @@ class Sema final {
1245512455
};
1245612456

1245712457
bool isKnownGoodSYCLDecl(const Decl *D);
12458+
void checkSYCLDeviceVarDecl(VarDecl *Var);
1245812459
void ConstructOpenCLKernel(FunctionDecl *KernelCallerFunc, MangleContext &MC);
1245912460
void MarkDevice();
1246012461

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12660,6 +12660,9 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
1266012660
}
1266112661
}
1266212662

12663+
if (getLangOpts().SYCLIsDevice)
12664+
checkSYCLDeviceVarDecl(var);
12665+
1266312666
// In Objective-C, don't allow jumps past the implicit initialization of a
1266412667
// local retaining variable.
1266512668
if (getLangOpts().ObjC &&

clang/lib/Sema/SemaSYCL.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,84 @@ bool Sema::isKnownGoodSYCLDecl(const Decl *D) {
200200
return false;
201201
}
202202

203+
static bool isZeroSizedArray(QualType Ty) {
204+
if (const auto *CATy = dyn_cast<ConstantArrayType>(Ty))
205+
return CATy->getSize() == 0;
206+
return false;
207+
}
208+
209+
static Sema::DeviceDiagBuilder
210+
emitDeferredDiagnosticAndNote(Sema &S, SourceRange Loc, unsigned DiagID,
211+
SourceRange UsedAtLoc) {
212+
Sema::DeviceDiagBuilder builder =
213+
S.SYCLDiagIfDeviceCode(Loc.getBegin(), DiagID);
214+
if (UsedAtLoc.isValid())
215+
S.SYCLDiagIfDeviceCode(UsedAtLoc.getBegin(), diag::note_sycl_used_here);
216+
return builder;
217+
}
218+
219+
static void checkSYCLVarType(Sema &S, QualType Ty, SourceRange Loc,
220+
llvm::DenseSet<QualType> Visited,
221+
SourceRange UsedAtLoc = SourceRange()) {
222+
// Not all variable types are supported inside SYCL kernels,
223+
// for example the quad type __float128 will cause errors in the
224+
// SPIR-V translation phase.
225+
// Here we check any potentially unsupported declaration and issue
226+
// a deferred diagnostic, which will be emitted iff the declaration
227+
// is discovered to reside in kernel code.
228+
// The optional UsedAtLoc param is used when the SYCL usage is at a
229+
// different location than the variable declaration and we need to
230+
// inform the user of both, e.g. struct member usage vs declaration.
231+
232+
//--- check types ---
233+
234+
// zero length arrays
235+
if (isZeroSizedArray(Ty))
236+
emitDeferredDiagnosticAndNote(S, Loc, diag::err_typecheck_zero_array_size,
237+
UsedAtLoc);
238+
239+
// Sub-reference array or pointer, then proceed with that type.
240+
while (Ty->isAnyPointerType() || Ty->isArrayType())
241+
Ty = QualType{Ty->getPointeeOrArrayElementType(), 0};
242+
243+
// __int128, __int128_t, __uint128_t, __float128
244+
if (Ty->isSpecificBuiltinType(BuiltinType::Int128) ||
245+
Ty->isSpecificBuiltinType(BuiltinType::UInt128) ||
246+
(Ty->isSpecificBuiltinType(BuiltinType::Float128) &&
247+
!S.Context.getTargetInfo().hasFloat128Type()))
248+
emitDeferredDiagnosticAndNote(S, Loc, diag::err_type_unsupported, UsedAtLoc)
249+
<< Ty.getUnqualifiedType().getCanonicalType();
250+
251+
//--- now recurse ---
252+
// Pointers complicate recursion. Add this type to Visited.
253+
// If already there, bail out.
254+
if (!Visited.insert(Ty).second)
255+
return;
256+
257+
if (const auto *ATy = dyn_cast<AttributedType>(Ty))
258+
return checkSYCLVarType(S, ATy->getModifiedType(), Loc, Visited);
259+
260+
if (const auto *RD = Ty->getAsRecordDecl()) {
261+
for (const auto &Field : RD->fields())
262+
checkSYCLVarType(S, Field->getType(), Field->getSourceRange(), Visited,
263+
Loc);
264+
} else if (const auto *FPTy = dyn_cast<FunctionProtoType>(Ty)) {
265+
for (const auto &ParamTy : FPTy->param_types())
266+
checkSYCLVarType(S, ParamTy, Loc, Visited);
267+
checkSYCLVarType(S, FPTy->getReturnType(), Loc, Visited);
268+
}
269+
}
270+
271+
void Sema::checkSYCLDeviceVarDecl(VarDecl *Var) {
272+
assert(getLangOpts().SYCLIsDevice &&
273+
"Should only be called during SYCL compilation");
274+
QualType Ty = Var->getType();
275+
SourceRange Loc = Var->getLocation();
276+
llvm::DenseSet<QualType> Visited;
277+
278+
checkSYCLVarType(*this, Ty, Loc, Visited);
279+
}
280+
203281
class MarkDeviceFunction : public RecursiveASTVisitor<MarkDeviceFunction> {
204282
public:
205283
MarkDeviceFunction(Sema &S)

clang/lib/Sema/SemaType.cpp

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,12 +1527,8 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) {
15271527
break;
15281528
case DeclSpec::TST_float128:
15291529
if (!S.Context.getTargetInfo().hasFloat128Type() &&
1530-
S.getLangOpts().SYCLIsDevice)
1531-
S.SYCLDiagIfDeviceCode(DS.getTypeSpecTypeLoc(),
1532-
diag::err_type_unsupported)
1533-
<< "__float128";
1534-
else if (!S.Context.getTargetInfo().hasFloat128Type() &&
1535-
!(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
1530+
!S.getLangOpts().SYCLIsDevice &&
1531+
!(S.getLangOpts().OpenMP && S.getLangOpts().OpenMPIsDevice))
15361532
S.Diag(DS.getTypeSpecTypeLoc(), diag::err_type_unsupported)
15371533
<< "__float128";
15381534
Result = Context.Float128Ty;
@@ -2350,12 +2346,6 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM,
23502346
<< ArraySize->getSourceRange();
23512347
ASM = ArrayType::Normal;
23522348
}
2353-
2354-
// Zero length arrays are disallowed in SYCL device code.
2355-
if (getLangOpts().SYCLIsDevice)
2356-
SYCLDiagIfDeviceCode(ArraySize->getBeginLoc(),
2357-
diag::err_typecheck_zero_array_size)
2358-
<< ArraySize->getSourceRange();
23592349
} else if (!T->isDependentType() && !T->isVariablyModifiedType() &&
23602350
!T->isIncompleteType() && !T->isUndeducedType()) {
23612351
// Is the array too large?

clang/test/SemaSYCL/deferred-diagnostics-emit.cpp

Lines changed: 98 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
//
33
// Ensure that the SYCL diagnostics that are typically deferred are correctly emitted.
44

5+
namespace std {
6+
class type_info;
7+
typedef __typeof__(sizeof(int)) size_t;
8+
} // namespace std
9+
510
// testing that the deferred diagnostics work in conjunction with the SYCL namespaces.
611
inline namespace cl {
712
namespace sycl {
813

914
template <typename name, typename Func>
1015
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
11-
// expected-note@+1 2{{called by 'kernel_single_task<AName, (lambda}}
16+
// expected-note@+1 3{{called by 'kernel_single_task<AName, (lambda}}
1217
kernelFunc();
1318
}
1419

@@ -18,11 +23,12 @@ __attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
1823
//variadic functions from SYCL kernels emit a deferred diagnostic
1924
void variadic(int, ...) {}
2025

26+
// there are more types like this checked in sycl-restrict.cpp
2127
int calledFromKernel(int a) {
2228
// expected-error@+1 {{zero-length arrays are not permitted in C++}}
2329
int MalArray[0];
2430

25-
// expected-error@+1 {{__float128 is not supported on this target}}
31+
// expected-error@+1 {{'__float128' is not supported on this target}}
2632
__float128 malFloat = 40;
2733

2834
//expected-error@+1 {{SYCL kernel cannot call a variadic function}}
@@ -31,21 +37,102 @@ int calledFromKernel(int a) {
3137
return a + 20;
3238
}
3339

40+
// defines (early and late)
41+
#define floatDef __float128
42+
#define int128Def __int128
43+
#define int128tDef __int128_t
44+
#define intDef int
45+
46+
//typedefs (late )
47+
typedef const __uint128_t megeType;
48+
typedef const __float128 trickyFloatType;
49+
typedef const __int128 tricky128Type;
50+
51+
//templated type (late)
52+
template <typename T>
53+
T bar() { return T(); };
54+
55+
//false positive. early incorrectly catches
56+
template <typename t>
57+
void foo(){};
58+
3459
// template used to specialize a function that contains a lambda that should
3560
// result in a deferred diagnostic being emitted.
36-
// HOWEVER, this is not working presently.
37-
// TODO: re-test after new deferred diagnostic system is merged.
38-
// restore the "FIX!!" tests below
3961

4062
template <typename T>
4163
void setup_sycl_operation(const T VA[]) {
4264

4365
cl::sycl::kernel_single_task<class AName>([]() {
44-
// FIX!! xpected-error@+1 {{zero-length arrays are not permitted in C++}}
45-
int OverlookedBadArray[0];
46-
47-
// FIX!! xpected-error@+1 {{__float128 is not supported on this target}}
48-
__float128 overlookedBadFloat = 40;
66+
// ======= Zero Length Arrays Not Allowed in Kernel ==========
67+
// expected-error@+1 {{zero-length arrays are not permitted in C++}}
68+
int MalArray[0];
69+
// expected-error@+1 {{zero-length arrays are not permitted in C++}}
70+
intDef MalArrayDef[0];
71+
// ---- false positive tests. These should not generate any errors.
72+
foo<int[0]>();
73+
std::size_t arrSz = sizeof(int[0]);
74+
75+
// ======= Float128 Not Allowed in Kernel ==========
76+
// expected-error@+1 {{'__float128' is not supported on this target}}
77+
__float128 malFloat = 40;
78+
// expected-error@+1 {{'__float128' is not supported on this target}}
79+
trickyFloatType malFloatTrick = 41;
80+
// expected-error@+1 {{'__float128' is not supported on this target}}
81+
floatDef malFloatDef = 44;
82+
// expected-error@+1 {{'__float128' is not supported on this target}}
83+
auto whatFloat = malFloat;
84+
// expected-error@+1 {{'__float128' is not supported on this target}}
85+
auto malAutoTemp5 = bar<__float128>();
86+
// expected-error@+1 {{'__float128' is not supported on this target}}
87+
auto malAutoTemp6 = bar<trickyFloatType>();
88+
// expected-error@+1 {{'__float128' is not supported on this target}}
89+
decltype(malFloat) malDeclFloat = 42;
90+
// ---- false positive tests
91+
std::size_t someSz = sizeof(__float128);
92+
foo<__float128>();
93+
94+
// ======= __int128 Not Allowed in Kernel ==========
95+
// expected-error@+1 {{'__int128' is not supported on this target}}
96+
__int128 malIntent = 2;
97+
// expected-error@+1 {{'__int128' is not supported on this target}}
98+
tricky128Type mal128Trick = 2;
99+
// expected-error@+1 {{'__int128' is not supported on this target}}
100+
int128Def malIntDef = 9;
101+
// expected-error@+1 {{'__int128' is not supported on this target}}
102+
auto whatInt128 = malIntent;
103+
// expected-error@+1 {{'__int128' is not supported on this target}}
104+
auto malAutoTemp = bar<__int128>();
105+
// expected-error@+1 {{'__int128' is not supported on this target}}
106+
auto malAutoTemp2 = bar<tricky128Type>();
107+
// expected-error@+1 {{'__int128' is not supported on this target}}
108+
decltype(malIntent) malDeclInt = 2;
109+
110+
// expected-error@+1 {{'__int128' is not supported on this target}}
111+
__int128_t malInt128 = 2;
112+
// expected-error@+1 {{'unsigned __int128' is not supported on this target}}
113+
__uint128_t malUInt128 = 3;
114+
// expected-error@+1 {{'unsigned __int128' is not supported on this target}}
115+
megeType malTypeDefTrick = 4;
116+
// expected-error@+1 {{'__int128' is not supported on this target}}
117+
int128tDef malInt2Def = 6;
118+
// expected-error@+1 {{'unsigned __int128' is not supported on this target}}
119+
auto whatUInt = malUInt128;
120+
// expected-error@+1 {{'__int128' is not supported on this target}}
121+
auto malAutoTemp3 = bar<__int128_t>();
122+
// expected-error@+1 {{'unsigned __int128' is not supported on this target}}
123+
auto malAutoTemp4 = bar<megeType>();
124+
// expected-error@+1 {{'__int128' is not supported on this target}}
125+
decltype(malInt128) malDeclInt128 = 5;
126+
127+
// ---- false positive tests These should not generate any errors.
128+
std::size_t i128Sz = sizeof(__int128);
129+
foo<__int128>();
130+
std::size_t u128Sz = sizeof(__uint128_t);
131+
foo<__int128_t>();
132+
133+
// ========= variadic
134+
//expected-error@+1 {{SYCL kernel cannot call a variadic function}}
135+
variadic(5);
49136
});
50137
}
51138

@@ -56,7 +143,7 @@ int main(int argc, char **argv) {
56143
// expected-error@+1 {{zero-length arrays are not permitted in C++}}
57144
int BadArray[0];
58145

59-
// expected-error@+1 {{__float128 is not supported on this target}}
146+
// expected-error@+1 {{'__float128' is not supported on this target}}
60147
__float128 badFloat = 40; // this SHOULD trigger a diagnostic
61148

62149
//expected-error@+1 {{SYCL kernel cannot call a variadic function}}

0 commit comments

Comments
 (0)