From efb38192b0b2437d5520f9c069277a16bfdc9c23 Mon Sep 17 00:00:00 2001 From: Matt Arsenault Date: Tue, 23 Jul 2013 01:23:36 +0000 Subject: [PATCH] Error on more illegal kernel argument types for OpenCL bool, half, pointers and structs / unions containing any of these are not allowed. Does not yet reject size_t and related integer types that are also disallowed. llvm-svn: 186908 --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 14 +- clang/include/clang/Sema/Sema.h | 1 + clang/lib/Sema/SemaDecl.cpp | 188 +++++++++++++++++++-- clang/test/SemaOpenCL/event_t.cl | 3 +- clang/test/SemaOpenCL/invalid-kernel-parameters.cl | 132 +++++++++++++++ clang/test/SemaOpenCL/invalid-kernel.cl | 2 +- 6 files changed, 316 insertions(+), 24 deletions(-) create mode 100644 clang/test/SemaOpenCL/invalid-kernel-parameters.cl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8e88f3d..ed70736 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6452,16 +6452,22 @@ def err_invalid_astype_of_different_size : Error< "invalid reinterpretation: sizes of %0 and %1 must match">; def err_static_kernel : Error< "kernel functions cannot be declared static">; -def err_opencl_ptrptr_kernel_arg : Error< - "kernel argument cannot be declared as a pointer to a pointer">; +def err_opencl_ptrptr_kernel_param : Error< + "kernel parameter cannot be declared as a pointer to a pointer">; def err_static_function_scope : Error< "variables in function scope cannot be declared static">; def err_opencl_bitfields : Error< "bitfields are not supported in OpenCL">; def err_opencl_vla : Error< "variable length arrays are not supported in OpenCL">; -def err_event_t_kernel_arg : Error< - "the event_t type cannot be used to declare a kernel function argument">; +def err_bad_kernel_param_type : Error< + "%0 cannot be used as the type of a kernel parameter">; +def err_record_with_pointers_kernel_param : Error< + "%select{struct|union}0 kernel parameters may not contain pointers">; +def note_within_field_of_type : Note< + "within field of type %0 declared here">; +def note_illegal_field_declared_here : Note< + "field of illegal %select{type|pointer type}0 %1 declared here">; def err_event_t_global_var : Error< "the event_t type cannot be used to declare a program scope variable">; def err_event_t_struct_field : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 9348c4a..f8e1968 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1454,6 +1454,7 @@ public: void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D); void ActOnStartFunctionDeclarator(); void ActOnEndFunctionDeclarator(); + NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, TypeSourceInfo *TInfo, LookupResult &Previous, diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 8b7c472..aca71b0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -6069,6 +6069,173 @@ void Sema::checkVoidParamDecl(ParmVarDecl *Param) { } } +enum OpenCLParamType { + ValidKernelParam, + PtrPtrKernelParam, + PtrKernelParam, + InvalidKernelParam, + RecordKernelParam +}; + +static OpenCLParamType getOpenCLKernelParameterType(QualType PT) { + if (PT->isPointerType()) { + QualType PointeeType = PT->getPointeeType(); + return PointeeType->isPointerType() ? PtrPtrKernelParam : PtrKernelParam; + } + + // TODO: Forbid the other integer types (size_t, ptrdiff_t...) when they can + // be used as builtin types. + + if (PT->isImageType()) + return PtrKernelParam; + + if (PT->isBooleanType()) + return InvalidKernelParam; + + if (PT->isEventT()) + return InvalidKernelParam; + + if (PT->isHalfType()) + return InvalidKernelParam; + + if (PT->isRecordType()) + return RecordKernelParam; + + return ValidKernelParam; +} + +static void checkIsValidOpenCLKernelParameter( + Sema &S, + Declarator &D, + ParmVarDecl *Param, + llvm::SmallPtrSet &ValidTypes) { + QualType PT = Param->getType(); + + // Cache the valid types we encounter to avoid rechecking structs that are + // used again + if (ValidTypes.count(PT.getTypePtr())) + return; + + switch (getOpenCLKernelParameterType(PT)) { + case PtrPtrKernelParam: + // OpenCL v1.2 s6.9.a: + // A kernel function argument cannot be declared as a + // pointer to a pointer type. + S.Diag(Param->getLocation(), diag::err_opencl_ptrptr_kernel_param); + D.setInvalidType(); + return; + + // OpenCL v1.2 s6.9.k: + // Arguments to kernel functions in a program cannot be declared with the + // built-in scalar types bool, half, size_t, ptrdiff_t, intptr_t, and + // uintptr_t or a struct and/or union that contain fields declared to be + // one of these built-in scalar types. + + case InvalidKernelParam: + // OpenCL v1.2 s6.8 n: + // A kernel function argument cannot be declared + // of event_t type. + S.Diag(Param->getLocation(), diag::err_bad_kernel_param_type) << PT; + D.setInvalidType(); + return; + + case PtrKernelParam: + case ValidKernelParam: + ValidTypes.insert(PT.getTypePtr()); + return; + + case RecordKernelParam: + break; + } + + // Track nested structs we will inspect + SmallVector VisitStack; + + // Track where we are in the nested structs. Items will migrate from + // VisitStack to HistoryStack as we do the DFS for bad field. + SmallVector HistoryStack; + HistoryStack.push_back((const FieldDecl *) 0); + + const RecordDecl *PD = PT->castAs()->getDecl(); + VisitStack.push_back(PD); + + assert(VisitStack.back() && "First decl null?"); + + do { + const Decl *Next = VisitStack.pop_back_val(); + if (!Next) { + assert(!HistoryStack.empty()); + // Found a marker, we have gone up a level + if (const FieldDecl *Hist = HistoryStack.pop_back_val()) + ValidTypes.insert(Hist->getType().getTypePtr()); + + continue; + } + + // Adds everything except the original parameter declaration (which is not a + // field itself) to the history stack. + const RecordDecl *RD; + if (const FieldDecl *Field = dyn_cast(Next)) { + HistoryStack.push_back(Field); + RD = Field->getType()->castAs()->getDecl(); + } else { + RD = cast(Next); + } + + // Add a null marker so we know when we've gone back up a level + VisitStack.push_back((const Decl *) 0); + + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) { + const FieldDecl *FD = *I; + QualType QT = FD->getType(); + + if (ValidTypes.count(QT.getTypePtr())) + continue; + + OpenCLParamType ParamType = getOpenCLKernelParameterType(QT); + if (ParamType == ValidKernelParam) + continue; + + if (ParamType == RecordKernelParam) { + VisitStack.push_back(FD); + continue; + } + + // OpenCL v1.2 s6.9.p: + // Arguments to kernel functions that are declared to be a struct or union + // do not allow OpenCL objects to be passed as elements of the struct or + // union. + if (ParamType == PtrKernelParam || ParamType == PtrPtrKernelParam) { + S.Diag(Param->getLocation(), + diag::err_record_with_pointers_kernel_param) + << PT->isUnionType() + << PT; + } else { + S.Diag(Param->getLocation(), diag::err_bad_kernel_param_type) << PT; + } + + S.Diag(PD->getLocation(), diag::note_within_field_of_type) + << PD->getDeclName(); + + // We have an error, now let's go back up through history and show where + // the offending field came from + for (ArrayRef::const_iterator I = HistoryStack.begin() + 1, + E = HistoryStack.end(); I != E; ++I) { + const FieldDecl *OuterField = *I; + S.Diag(OuterField->getLocation(), diag::note_within_field_of_type) + << OuterField->getType(); + } + + S.Diag(FD->getLocation(), diag::note_illegal_field_declared_here) + << QT->isPointerType() + << QT; + D.setInvalidType(); + return; + } + } while (!VisitStack.empty()); +} + NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -6803,27 +6970,12 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, diag::err_expected_kernel_void_return_type); D.setInvalidType(); } - + + llvm::SmallPtrSet ValidTypes; for (FunctionDecl::param_iterator PI = NewFD->param_begin(), PE = NewFD->param_end(); PI != PE; ++PI) { ParmVarDecl *Param = *PI; - QualType PT = Param->getType(); - - // OpenCL v1.2 s6.9.a: - // A kernel function argument cannot be declared as a - // pointer to a pointer type. - if (PT->isPointerType() && PT->getPointeeType()->isPointerType()) { - Diag(Param->getLocation(), diag::err_opencl_ptrptr_kernel_arg); - D.setInvalidType(); - } - - // OpenCL v1.2 s6.8 n: - // A kernel function argument cannot be declared - // of event_t type. - if (PT->isEventT()) { - Diag(Param->getLocation(), diag::err_event_t_kernel_arg); - D.setInvalidType(); - } + checkIsValidOpenCLKernelParameter(*this, D, Param, ValidTypes); } } diff --git a/clang/test/SemaOpenCL/event_t.cl b/clang/test/SemaOpenCL/event_t.cl index 06197d0..5ab5c10 100644 --- a/clang/test/SemaOpenCL/event_t.cl +++ b/clang/test/SemaOpenCL/event_t.cl @@ -8,10 +8,11 @@ constant struct evt_s { void foo(event_t evt); // expected-note {{passing argument to parameter 'evt' here}} -void kernel ker(event_t argevt) { // expected-error {{the event_t type cannot be used to declare a kernel function argument}} +void kernel ker(event_t argevt) { // expected-error {{'event_t' cannot be used as the type of a kernel parameter}} event_t e; constant event_t const_evt; // expected-error {{the event_t type can only be used with __private address space qualifier}} foo(e); foo(0); foo(5); // expected-error {{passing 'int' to parameter of incompatible type 'event_t'}} } + diff --git a/clang/test/SemaOpenCL/invalid-kernel-parameters.cl b/clang/test/SemaOpenCL/invalid-kernel-parameters.cl new file mode 100644 index 0000000..de32eae --- /dev/null +++ b/clang/test/SemaOpenCL/invalid-kernel-parameters.cl @@ -0,0 +1,132 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#pragma OPENCL EXTENSION cl_khr_fp16 : enable + + +// Disallowed: parameters with type +// bool, half, size_t, ptrdiff_t, intptr_t, and uintptr_t +// or a struct / union with any of these types in them + +// TODO: Ban int types, size_t, ptrdiff_t ... + +kernel void bool_arg(bool x) { } // expected-error{{'bool' cannot be used as the type of a kernel parameter}} + +kernel void half_arg(half x) { } // expected-error{{'half' cannot be used as the type of a kernel parameter}} + +typedef struct ContainsBool // expected-note{{within field of type 'ContainsBool' declared here}} +{ + bool x; // expected-note{{field of illegal type 'bool' declared here}} +} ContainsBool; + +kernel void bool_in_struct_arg(ContainsBool x) { } // expected-error{{'ContainsBool' (aka 'struct ContainsBool') cannot be used as the type of a kernel parameter}} + + + +typedef struct FooImage2D // expected-note{{within field of type 'FooImage2D' declared here}} +{ + image2d_t imageField; // expected-note{{field of illegal type 'image2d_t' declared here}} +} FooImage2D; + +kernel void image_in_struct_arg(FooImage2D arg) { } // expected-error{{struct kernel parameters may not contain pointers}} + +typedef struct Foo // expected-note{{within field of type 'Foo' declared here}} +{ + int* ptrField; // expected-note{{field of illegal pointer type 'int *' declared here}} +} Foo; + +kernel void pointer_in_struct_arg(Foo arg) { } // expected-error{{struct kernel parameters may not contain pointers}} + +typedef union FooUnion // expected-note{{within field of type 'FooUnion' declared here}} +{ + int* ptrField; // expected-note{{field of illegal pointer type 'int *' declared here}} +} FooUnion; + +kernel void pointer_in_union_arg(FooUnion arg) { }// expected-error{{union kernel parameters may not contain pointers}} + +typedef struct NestedPointer // expected-note 2 {{within field of type 'NestedPointer' declared here}} +{ + int x; + struct InnerNestedPointer + { + int* ptrField; // expected-note 3 {{field of illegal pointer type 'int *' declared here}} + } inner; // expected-note 3 {{within field of type 'struct InnerNestedPointer' declared here}} +} NestedPointer; + +kernel void pointer_in_nested_struct_arg(NestedPointer arg) { }// expected-error{{struct kernel parameters may not contain pointers}} + +struct NestedPointerComplex // expected-note{{within field of type 'NestedPointerComplex' declared here}} +{ + int foo; + float bar; + + struct InnerNestedPointerComplex + { + int innerFoo; + int* innerPtrField; // expected-note{{field of illegal pointer type 'int *' declared here}} + } inner; // expected-note{{within field of type 'struct InnerNestedPointerComplex' declared here}} + + float y; + float z[4]; +}; + +kernel void pointer_in_nested_struct_arg_complex(struct NestedPointerComplex arg) { }// expected-error{{struct kernel parameters may not contain pointers}} + +typedef struct NestedBool // expected-note 2 {{within field of type 'NestedBool' declared here}} +{ + int x; + struct InnerNestedBool + { + bool boolField; // expected-note 2 {{field of illegal type 'bool' declared here}} + } inner; // expected-note 2 {{within field of type 'struct InnerNestedBool' declared here}} +} NestedBool; + +kernel void bool_in_nested_struct_arg(NestedBool arg) { } // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used as the type of a kernel parameter}} + +// Warning emitted again for argument used in other kernel +kernel void bool_in_nested_struct_arg_again(NestedBool arg) { } // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used as the type of a kernel parameter}} + + +// Check for note with a struct not defined inside the struct +typedef struct NestedBool2Inner +{ + bool boolField; // expected-note{{field of illegal type 'bool' declared here}} +} NestedBool2Inner; + +typedef struct NestedBool2 // expected-note{{within field of type 'NestedBool2' declared here}} +{ + int x; + NestedBool2Inner inner; // expected-note{{within field of type 'NestedBool2Inner' (aka 'struct NestedBool2Inner') declared here}} +} NestedBool2; + +kernel void bool_in_nested_struct_2_arg(NestedBool2 arg) { } // expected-error{{'NestedBool2' (aka 'struct NestedBool2') cannot be used as the type of a kernel parameter}} + + +struct InnerInner +{ + int* foo; + bool x; +}; + +struct Valid +{ + float c; + float d; +}; + +struct Inner +{ + struct Valid v; + struct InnerInner a; + struct Valid g; + struct InnerInner b; +}; + +struct AlsoUser // expected-note{{within field of type 'AlsoUser' declared here}} +{ + float x; + struct Valid valid1; + struct Valid valid2; + struct NestedPointer aaaa; // expected-note{{within field of type 'struct NestedPointer' declared here}} +}; + +kernel void pointer_in_nested_struct_arg_2(struct Valid valid, struct NestedPointer arg, struct AlsoUser also) { } // expected-error 2 {{struct kernel parameters may not contain pointers}} diff --git a/clang/test/SemaOpenCL/invalid-kernel.cl b/clang/test/SemaOpenCL/invalid-kernel.cl index fb8ce58..3eb2f71 100644 --- a/clang/test/SemaOpenCL/invalid-kernel.cl +++ b/clang/test/SemaOpenCL/invalid-kernel.cl @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -verify %s -kernel void no_ptrptr(global int **i) { } // expected-error{{kernel argument cannot be declared as a pointer to a pointer}} +kernel void no_ptrptr(global int **i) { } // expected-error{{kernel parameter cannot be declared as a pointer to a pointer}} kernel int bar() { // expected-error {{kernel must have void return type}} return 6; -- 2.7.4