This is an archive of the discontinued LLVM Phabricator instance.

[OpenCL] Blocks cannot capture/reference another block
ClosedPublic

Authored by Anastasia on Feb 9 2017, 7:34 AM.

Details

Reviewers
yaxunl
Summary

Adding the last restriction from s6.12.5 OpenCL C v2.0.

"A Block cannot reference or capture another Block variable declared in the outer scope".

Diff Detail

Event Timeline

Anastasia created this revision.Feb 9 2017, 7:34 AM
Anastasia edited reviewers, added: yaxunl; removed: pekka.jaaskelainen.Feb 10 2017, 2:43 AM
Anastasia edited subscribers, added: pekka.jaaskelainen; removed: yaxunl.
Nicola added a subscriber: Nicola.Feb 10 2017, 8:15 AM

Looking at "Example 4" in the standard it looks like this should also be illegal.

int (^block1)(void) = ^int {return 1;};
int foo() { return block1(); }

__kernel void k(global int *z)
{
 int (^block2)(void) = ^int {
  return foo(); // expected-error {{cannot refer to a block inside block}}
 }; 
}

Unless I missed something it's not erroring in this case.

yaxunl edited edge metadata.Feb 10 2017, 10:28 AM

Looking at "Example 4" in the standard it looks like this should also be illegal.

int (^block1)(void) = ^int {return 1;};
int foo() { return block1(); }

__kernel void k(global int *z)
{
 int (^block2)(void) = ^int {
  return foo(); // expected-error {{cannot refer to a block inside block}}
 }; 
}

Unless I missed something it's not erroring in this case.

To diagnose this needs to traverse the AST tree. I think it is too much to do it during parsing.

It may be done through static analysis though.

Looking at "Example 4" in the standard it looks like this should also be illegal.

int (^block1)(void) = ^int {return 1;};
int foo() { return block1(); }

__kernel void k(global int *z)
{
 int (^block2)(void) = ^int {
  return foo(); // expected-error {{cannot refer to a block inside block}}
 }; 
}

Unless I missed something it's not erroring in this case.

To diagnose this needs to traverse the AST tree. I think it is too much to do it during parsing.

It may be done through static analysis though.

Looking at "Example 4" in the standard it looks like this should also be illegal.

int (^block1)(void) = ^int {return 1;};
int foo() { return block1(); }

__kernel void k(global int *z)
{
 int (^block2)(void) = ^int {
  return foo(); // expected-error {{cannot refer to a block inside block}}
 }; 
}

Unless I missed something it's not erroring in this case.

To diagnose this needs to traverse the AST tree. I think it is too much to do it during parsing.

It may be done through static analysis though.

Yes, it will require quite some expensive checks to implement visiting the AST to trace back the block variables declared in other functions. Additionally this example doesn't seem to cause any issues really. It is mainly in the case with blocks captured from the stack:

kernel void foo() {
  bl2_t bl1 = ^(int i) {
    return 1;
  };
  void (^bl2)(void) = ^{
    int i = bl1(1); // expected-error {{cannot refer to a block inside block}}
  };
}

}
for which ObjC implementation creates copy/destroy helpers that use some symbols that are supposed to be defined elsewhere (presumably in the ObjC runtime) which causes issue in OpenCL because we suddenly end up with undefined symbols. I am guessing this is needed in order to promote stack allocated blocks into heap. Although the copy itself doesn't happen in our cases.

test/SemaOpenCL/invalid-block.cl
71

v0 is not used!

yaxunl accepted this revision.Feb 13 2017, 11:01 AM

Please fix the test. Otherwise LGTM. Thanks!

This revision is now accepted and ready to land.Feb 13 2017, 11:01 AM
Anastasia closed this revision.Feb 16 2017, 6:28 AM

Committed in r295307!