Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -6441,16 +6441,18 @@ "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 to declare a kernel function parameter">; +def err_struct_with_pointers_kernel_param : Error< + "struct or union kernel parameters may not contain OpenCL objects">; 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< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1446,6 +1446,8 @@ void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D); void ActOnStartFunctionDeclarator(); void ActOnEndFunctionDeclarator(); + + void checkIsValidOpenCLKernelArgument(Declarator &D, ParmVarDecl *P); NamedDecl* ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, TypeSourceInfo *TInfo, LookupResult &Previous, Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -6072,6 +6072,172 @@ } } +enum OpenCLParamType { + ValidKernelParam, + PtrPtrKernelParam, + BoolKernelParam, + IntTypeKernelParam, + EventKernelParam, + HalfKernelParam, + PtrKernelParam, + RecordKernelParam +}; + +static OpenCLParamType getOpenCLKernelParameterType(QualType PT) { + if (PT->isPointerType()) { + QualType PointeeType = PT->getPointeeType(); + return PointeeType->isPointerType() ? PtrPtrKernelParam : PtrKernelParam; + } + + if (PT->isImageType()) + return PtrKernelParam; + + if (PT->isIntegerType()) { + if (PT->isBooleanType()) + return BoolKernelParam; + + while (const TypedefType *Typedef = dyn_cast(PT)) { + const IdentifierInfo *Identifier = Typedef->getDecl()->getIdentifier(); + StringRef Name = Identifier->getName(); + + if (Name == "size_t" || + Name == "ptrdiff_t" || + Name == "intptr_t" || + Name == "uintptr_t") { + return IntTypeKernelParam; + } + + PT = Typedef->getDecl()->getUnderlyingType(); + } + } + + if (PT->isEventT()) + return EventKernelParam; + + if (PT->isHalfType()) + return HalfKernelParam; + + if (PT->isRecordType()) + return RecordKernelParam; + + return ValidKernelParam; +} + +static void checkIsValidOpenCLKernelParameter(Sema &S, + Declarator &D, + ParmVarDecl *Param) { + QualType PT = Param->getType(); + + 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; + + case BoolKernelParam: + case HalfKernelParam: + case IntTypeKernelParam: + // 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 EventKernelParam: + // 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: + 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; + + 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) { + // Found a marker, we have gone up a level + HistoryStack.pop_back(); + 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(); + + 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. + unsigned DiagID + = (ParamType == PtrKernelParam || ParamType == PtrPtrKernelParam) + ? diag::err_struct_with_pointers_kernel_param + : diag::err_bad_kernel_param_type; + + S.Diag(Param->getLocation(), DiagID) << PT; + S.Diag(PD->getLocation(), diag::note_previous_decl) + << 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(), + E = HistoryStack.end(); I != E; ++I) { + const FieldDecl *OuterField = *I; + S.Diag(OuterField->getLocation(), diag::note_member_declared_here) + << OuterField->getDeclName(); + } + + S.Diag(FD->getLocation(), diag::note_member_declared_here) + << FD->getDeclName(); + D.setInvalidType(); + } + } while (!VisitStack.empty()); +} + NamedDecl* Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, TypeSourceInfo *TInfo, LookupResult &Previous, @@ -6804,34 +6970,18 @@ Diag(D.getIdentifierLoc(), diag::err_static_kernel); D.setInvalidType(); } - + // OpenCL v1.2, s6.9 -- Kernels can only have return type void. if (!NewFD->getResultType()->isVoidType()) { Diag(D.getIdentifierLoc(), diag::err_expected_kernel_void_return_type); D.setInvalidType(); } - + 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); } } Index: test/SemaOpenCL/event_t.cl =================================================================== --- test/SemaOpenCL/event_t.cl +++ test/SemaOpenCL/event_t.cl @@ -8,7 +8,7 @@ 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 to declare a kernel function 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); Index: test/SemaOpenCL/invalid-kernel-parameters.cl =================================================================== --- /dev/null +++ test/SemaOpenCL/invalid-kernel-parameters.cl @@ -0,0 +1,122 @@ +// 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 + + +// These would normally come from a header with the OpenCL builtins +typedef unsigned int size_t; +typedef size_t ptrdiff_t; +typedef size_t intptr_t; +typedef size_t uintptr_t; + + + +typedef size_t size_t_typedefed; + +kernel void size_t_typedef_arg(size_t_typedefed x) { } // expected-error{{'size_t_typedefed' (aka 'unsigned int') cannot be used to declare a kernel function parameter}} + + +kernel void bool_arg(bool x) { } // expected-error{{'bool' cannot be used to declare a kernel function parameter}} + +kernel void half_arg(half x) { } // expected-error{{'half' cannot be used to declare a kernel function parameter}} + +kernel void size_t_arg(size_t x) { } // expected-error{{'size_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}} + +kernel void ptrdiff_t_arg(ptrdiff_t x) { } // expected-error{{ptrdiff_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}} + +kernel void intptr_t_arg(intptr_t x) { } // expected-error{{'intptr_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}} + +kernel void uintptr_t_arg(uintptr_t x) { } // expected-error{{uintptr_t' (aka 'unsigned int') cannot be used to declare a kernel function parameter}} + +typedef struct ContainsBool // expected-note{{'ContainsBool' declared here}} +{ + bool x; // expected-note{{member 'x' declared here}} +} ContainsBool; + +kernel void bool_in_struct_arg(ContainsBool x) { } // expected-error{{'ContainsBool' (aka 'struct ContainsBool') cannot be used to declare a kernel function parameter}} + + + +typedef struct FooImage2D // expected-note{{'FooImage2D' declared here}} +{ + image2d_t imageField; // expected-note{{member 'imageField' declared here}} +} FooImage2D; + +kernel void image_in_struct_arg(FooImage2D arg) { } // expected-error{{struct or union kernel parameters may not contain OpenCL object}} + +typedef struct Foo // expected-note{{'Foo' declared here}} +{ + int* ptrField; // expected-note{{member 'ptrField' declared here}} +} Foo; + +kernel void pointer_in_struct_arg(Foo arg) { } // expected-error{{struct or union kernel parameters may not contain OpenCL object}} + +typedef union FooUnion // expected-note{{'FooUnion' declared here}} +{ + int* ptrField; // expected-note{{member 'ptrField' declared here}} +} FooUnion; + +kernel void pointer_in_union_arg(FooUnion arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL object}} + +typedef struct NestedPointer // expected-note{{'NestedPointer' declared here}} +{ + int x; + struct InnerNestedPointer + { + int* ptrField; // expected-note{{member 'ptrField' declared here}} + } inner; // expected-note{{member 'inner' declared here}} +} NestedPointer; + +kernel void pointer_in_nested_struct_arg(NestedPointer arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}} + +struct NestedPointerComplex // expected-note{{'NestedPointerComplex' declared here}} +{ + int foo; + float bar; + + struct InnerNestedPointerComplex + { + int innerFoo; + int* innerPtrField; // expected-note{{member 'innerPtrField' declared here}} + } inner; // expected-note{{member 'inner' declared here}} + + float y; + float z[4]; +}; + +kernel void pointer_in_nested_struct_arg_complex(struct NestedPointerComplex arg) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}} + +typedef struct NestedBool // expected-note 2 {{'NestedBool' declared here}} +{ + int x; + struct InnerNestedBool + { + bool boolField; // expected-note 2 {{member 'boolField' declared here}} + } inner; // expected-note 2 {{member 'inner' declared here}} +} NestedBool; + +kernel void bool_in_nested_struct_arg(NestedBool arg) { } // expected-error{{'NestedBool' (aka 'struct NestedBool') cannot be used to declare a kernel function 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 to declare a kernel function parameter}} + + +// Check for note with a struct not defined inside the struct +typedef struct NestedBool2Inner +{ + bool boolField; // expected-note{{member 'boolField' declared here}} +} NestedBool2Inner; + +typedef struct NestedBool2 // expected-note{{'NestedBool2' declared here}} +{ + int x; + NestedBool2Inner inner; // expected-note{{member 'inner' declared here}} +} NestedBool2; + +kernel void bool_in_nested_struct_2_arg(NestedBool2 arg) { } // expected-error{{'NestedBool2' (aka 'struct NestedBool2') cannot be used to declare a kernel function parameter}} +