This is an archive of the discontinued LLVM Phabricator instance.

Error on illegal OpenCL kernel argument types
ClosedPublic

Authored by arsenm on Jun 26 2013, 6:47 PM.

Details

Reviewers
rsmith
Summary

OpenCL disallows various types (bool, size_t, pointers, or structs containing them) to be used for kernel arguments, but currently they are accepted. The error message in the struct case is currently a bit awkward since it doesn't describe which struct member is at fault for not allowing the kernel argument.

Diff Detail

Event Timeline

+ if (const TypedefType *Typedef = dyn_cast<TypedefType>(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 false;
+ }
+ }

No. It's completely insane to print an error message based on typedef
sugar. Someone could write "typedef size_t mysize_t;", or "typedef
__typeof(sizeof(int)) mysize_t;", or "#if SIZE_MAX == UINT_MAX [...]
typdef unsigned mysize_t [...]".

+static const RecordType *getAsStructOrUnionType(QualType QT) {
+ const RecordType *RT = QT->getAsStructureType();
+ if (!RT)
+ RT = QT->getAsUnionType();
+
+ return RT;
+}

This is equivalent to QT->getAs<RecordType>().

+ if (!isValidOpenCLKernelArgumentType(QT)) {
+ TODO: A more specific warning about which struct members forbid this
+
would be useful
+ Diag(Param->getLocation(), diag::err_bad_kernel_arg_type) << PT;
+ D.setInvalidType();

Diag(FD->getLocation(), diag::note_field_declared_here);

Please just fix this.

+kernel void bool_arg(bool x) // expected-error{{'bool' cannot be used
to declare a kernel function argument}}
+{
+
+}

"kernel void bool_arg(bool x) {}" would save 3 lines of useless whitespace.

-Eli

rsmith added inline comments.Jul 8 2013, 2:38 PM
lib/Sema/SemaDecl.cpp
5917

"...ParameterType" not "...ArgumentType".

5919

I assume this part of the OpenCL spec is defective and it means "Parameters" not "Arguments" here?

5927–5937

OpenCL 6.1.1 seems to imply that size_t, ptrdiff_t, intptr_t and uintptr_t are distinct built-in types in OpenCL, rather than being typedefs for some standard integer type, and it's the underlying types that we should be checking for here, not the typedefs. "typedef size_t my_size_t;" should also be prohibited here (as should, presumably, a typedef for __typeof(sizeof(0))).

However, the OpenCL spec is unclear, so I'm somewhat guessing... :(

5952–5958

This is just QT->getAs<RecordType>().

5960

This doesn't need to be a Sema member; a static non-member function would be preferred.

5966–5969

It would seem cleaner to fold both this pointer check and the one below into isValidOpenCLKernelArgumentType. You can return an enum value from there to indicate the flavor of problem, and fold together the diagnostics with a %select. You can also pass a value into this function to indicate whether you're checking a direct argument (where only a pointer-to-pointer is blocked) or a field of an argument (where any pointer is blocked).

5989–5990

Have you considered caching the result of this check? Or at least, storing a set of the types for which you've performed the check -- we don't really need to diagnose the same type more than once.

6000–6002

Maybe reformulate the queue as a recursive walk, and produce notes on the way back up after diagnosing a problem?

arsenm updated this revision to Unknown Object (????).Jul 8 2013, 9:16 PM

Partially address review comments. Remove Sema member, rename argument to parameter, combine parameter check function, and use diag::note_member_declared_here on structs with invalid types in them. Does not yet show the chain of fields that are problematic, just the lowest one. Also update event_t test for changed warning.

lib/Sema/SemaDecl.cpp
5919

I just copied the text out of the spec

5927–5937

I wasn't really sure what to do about these. I didn't see an equivalent in the builtin type enums I found. It sort of depends on the library implementation. Apple and libclc at least implicitly include a header before the program which has these typedefs in them.

For example, the Apple header defines them as
typedef typeof(((int*)0)-((int*)0)) ptrdiff_t;
typedef SIZE_TYPE size_t;
typedef SIZE_TYPE uintptr_t;
typedef PTRDIFF_TYPE intptr_t;
typedef size_t event_t;

I don't actually see them defined in libclc, but size_t is used so I have no idea where that is coming from.

5989–5990

The type itself isn't the problem, it's only when used as a kernel argument. I don't think only diagnosing it on the first parameter found and then not on a second use as another argument would make much sense.

arsenm updated this revision to Unknown Object (????).Jul 12 2013, 11:17 AM

Make warning report which nested struct fields invalidate the argument, reporting first the outermost struct, followed by the nested fields from the outermost to the first found illegal one.

For example, for a nested struct case like this:
struct InnerInner
{

int* foo;

};

struct Valid
{

float c;
float d;

};

struct Inner
{

struct Valid v;
struct InnerInner a;
struct Valid g;
struct InnerInner b;

};

struct NestedPointer
{

int x;
struct Inner inner;

};

kernel void pointer_in_nested_struct_arg(struct NestedPointer arg) { }

The diagnostic produced is:

error: struct or union kernel parameters may not contain OpenCL objects
kernel void pointer_in_nested_struct_arg(struct NestedPointer arg, struct NestedPointer second) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}}

^

test/SemaOpenCL/arst2.cl:29:8: note: 'NestedPointer' declared here
struct NestedPointer

^

test/SemaOpenCL/arst2.cl:32:16: note: member 'inner' declared here

struct Inner inner;
             ^

test/SemaOpenCL/arst2.cl:26:21: note: member 'b' declared here

struct InnerInner b;
                  ^

test/SemaOpenCL/arst2.cl:12:8: note: member 'foo' declared here

int* foo;
     ^

For the struct diagnostic, it's not very clear what the notes mean (sure, there are members declared there, but why are you pointing them out?). Maybe you could produce something like:

test/SemaOpenCL/arst2.cl:35:62: error: 'NestedPointer' cannot be used as the type of a kernel function parameter because it contains a field of pointer type
        kernel void pointer_in_nested_struct_arg(struct NestedPointer arg, struct NestedPointer second) { }// expected-error{{struct or union kernel parameters may not contain OpenCL objects}}
                                                                      ^
test/SemaOpenCL/arst2.cl:32:16: note: within field of type 'Inner' declared here
          struct Inner inner;
                       ^
test/SemaOpenCL/arst2.cl:26:21: note: within field of type 'InnerInner' declared here
          struct InnerInner b;
                            ^
test/SemaOpenCL/arst2.cl:12:8: note: field of pointer type 'int*' declared here
          int* foo;
               ^
include/clang/Basic/DiagnosticSemaKinds.td
6453

"%0 cannot be used as the type of a kernel function parameter" would be better.

6454–6455

Instead of "struct or union", please say which, and instead of "OpenCL objects" (which is not correct in several cases) please include the actual type as you do in the previous diagnostic.

include/clang/Sema/Sema.h
1449–1450

Remove this, you don't define such a Sema member any more.

lib/Sema/SemaDecl.cpp
6178–6196

This would be cleaner if you made this function recursive.

6236

I don't think this is necessary.

arsenm updated this revision to Unknown Object (????).Jul 19 2013, 3:33 PM

Change wording of nested struct notes, remove old Sema member declaration, fix pop_back on empty vector.

Add cache of valid types encountered (but only is within the same kernel declaration).

error: struct kernel parameters may not contain OpenCL objects
kernel void pointer_in_nested_struct_arg(struct NestedPointer arg) { }

^

/tmp/arst.cl:23:8: note: within field of type 'NestedPointer' declared here
struct NestedPointer

^

/tmp/arst.cl:27:14: note: within field of type 'struct Inner' declared here
struct Inner inner;

^

/tmp/arst.cl:20:19: note: within field of type 'struct InnerInner' declared here
struct InnerInner b;

^

/tmp/arst.cl:4:6: note: field of illegal pointer type 'int *' declared here
int* foo;

^

Or for a non-pointer
error: 'struct NestedBool' cannot be used as the type of a kernel parameter
kernel void bool_in_nested_struct_arg(struct NestedBool arg) { }

^

/tmp/arst.cl:23:8: note: within field of type 'NestedBool' declared here
struct NestedBool

^

/tmp/arst.cl:27:14: note: within field of type 'struct Inner' declared here
struct Inner inner;

^

/tmp/arst.cl:20:19: note: within field of type 'struct InnerInner' declared here
struct InnerInner b;

^

/tmp/arst.cl:4:6: note: field of illegal type 'bool' declared here
bool foo;

Couple of minor tweaks, then LGTM

include/clang/Basic/DiagnosticSemaKinds.td
6459–6460

"may not contain OpenCL objects" doesn't seem appropriate here. "may not contain pointers" seems to match the cases where this diagnostic is emitted.

6463–6464

Please use a %select in the place of %0 here (we try to keep our diagnostics translatable, even though we don't support localization yet).

arsenm updated this revision to Unknown Object (????).Jul 19 2013, 4:54 PM

Use select, say pointers instead of OpenCL objects

rsmith accepted this revision.Jul 22 2013, 5:56 PM

LGTM. Do you have commit access?

arsenm closed this revision.Jul 22 2013, 6:26 PM

Yes, committed r186908.