This is an archive of the discontinued LLVM Phabricator instance.

[OpenCL] Remove block invoke function from emitted block literal struct
ClosedPublic

Authored by yaxunl on Feb 26 2018, 1:33 PM.

Details

Summary

OpenCL runtime tracks the invoke function emitted for
any block expression. Due to restrictions on blocks in
OpenCL (v2.0 s6.12.5), it is always possible to know the
block invoke function when emitting call of block expression
or __enqueue_kernel builtin functions. Since __enqueu_kernel
already has an argument for the invoke function, it is redundant
to have invoke function member in the llvm block literal structure.

This patch removes invoke function from the llvm block literal
structure. It also removes the bitcast of block invoke function
to the "generic block literal type" which is useless for OpenCL.

This will save some space for the kernel argument, and also
eliminate some store instructions.

Diff Detail

Repository
rC Clang

Event Timeline

yaxunl created this revision.Feb 26 2018, 1:33 PM
yaxunl edited the summary of this revision. (Show Details)Feb 26 2018, 1:34 PM
nhaehnle removed a subscriber: nhaehnle.Feb 27 2018, 7:59 AM
bader accepted this revision.Mar 6 2018, 2:39 AM

Hi Sam,

Sorry for the delay. LGTM, I have only minor refactoring suggestion.

Thanks,
Alexey

lib/CodeGen/CGBlocks.cpp
1070–1072

I think it would be more readable if we merge this if statement with the if statement at the line #1103.
It's used to initialize FuncPtr for non-OpenCL languages and the first use of this variable is in the else block of if statement at the line #1103.
If I didn't miss something it should reasonable to combine this if block with 'else' block at the line #1106.

This revision is now accepted and ready to land.Mar 6 2018, 2:39 AM
yaxunl added inline comments.Mar 6 2018, 9:39 AM
lib/CodeGen/CGBlocks.cpp
1070–1072

BlockPtr is used on line 1093, so it cannot be moved to line 1106.

This revision was automatically updated to reflect the committed changes.
svenvh added a subscriber: svenvh.Aug 17 2018, 9:26 AM

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for the delay. I think block should not be allowed as function argument since this generally leads indirect function calls therefore requires support of function pointer. It will rely on optimizations to get rid of indirect function calls.

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for the delay. I think block should not be allowed as function argument since this generally leads indirect function calls therefore requires support of function pointer. It will rely on optimizations to get rid of indirect function calls.

The idea was to allow blocks as function parameters because they are statically known at each function call. This can be resolved later at IR level instead of frontend. I am also not sure there can be other corner cases where it wouldn't work in Clang since we can't leverage analysis passes here. I feel this might be a risky thing to do in Clang. Currently it doesn't work for the examples provided and therefore breaking the compliance with the spec.

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for the delay. I think block should not be allowed as function argument since this generally leads indirect function calls therefore requires support of function pointer. It will rely on optimizations to get rid of indirect function calls.

The idea was to allow blocks as function parameters because they are statically known at each function call. This can be resolved later at IR level instead of frontend. I am also not sure there can be other corner cases where it wouldn't work in Clang since we can't leverage analysis passes here. I feel this might be a risky thing to do in Clang. Currently it doesn't work for the examples provided and therefore breaking the compliance with the spec.

I agree this patch should be reverted for conformance to spec. I will do that. Thanks.

Ping! Do you still plan to do this? :)

Ping! Do you still plan to do this? :)

Sorry I caught up in something else. Since there are some subsequent commits, it may take some efforts to revert it.

Ping! Do you still plan to do this? :)

Sorry I caught up in something else. Since there are some subsequent commits, it may take some efforts to revert it.

No problem. Let me know if help is needed. :)

Ping! Do you still plan to do this? :)

Sorry I caught up in something else. Since there are some subsequent commits, it may take some efforts to revert it.

Let me know if you need help. I am happy to do this myself.

svenvh added a comment.Oct 2 2018, 6:06 AM

Reverted in r343582, test added in r343583.

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for the delay. I think block should not be allowed as function argument since this generally leads indirect function calls therefore requires support of function pointer. It will rely on optimizations to get rid of indirect function calls.

The idea was to allow blocks as function parameters because they are statically known at each function call. This can be resolved later at IR level instead of frontend. I am also not sure there can be other corner cases where it wouldn't work in Clang since we can't leverage analysis passes here. I feel this might be a risky thing to do in Clang. Currently it doesn't work for the examples provided and therefore breaking the compliance with the spec.

The spec is not clear about what is "statically determinable". To me, in the example provided we can not resolve the bl() call without inlining foo, even at IR level. As Sam noted, that leads to indirect call and require support of functions pointers.
I see a contradiction in the spec in allowing blocks to be a function argument and disallowing function pointers at the same time. I think maybe the spec should be changed to clarify existing restrictions or add more restrictions for cases when blocks are passed as function argument.

Sorry for digging up an old commit...

Apparently this broke block arguments, e.g. the following test case:

int foo(int (^ bl)(void)) {
  return bl();
}

int get21() {
  return foo(^{return 21;});
}

int get42() {
  return foo(^{return 42;});
}

In particular, the VarDecl that getBlockExpr() sees doesn't have an initializer when the called block comes from an argument (causing clang to crash).

Sorry for the delay. I think block should not be allowed as function argument since this generally leads indirect function calls therefore requires support of function pointer. It will rely on optimizations to get rid of indirect function calls.

The idea was to allow blocks as function parameters because they are statically known at each function call. This can be resolved later at IR level instead of frontend. I am also not sure there can be other corner cases where it wouldn't work in Clang since we can't leverage analysis passes here. I feel this might be a risky thing to do in Clang. Currently it doesn't work for the examples provided and therefore breaking the compliance with the spec.

The spec is not clear about what is "statically determinable". To me, in the example provided we can not resolve the bl() call without inlining foo, even at IR level. As Sam noted, that leads to indirect call and require support of functions pointers.
I see a contradiction in the spec in allowing blocks to be a function argument and disallowing function pointers at the same time. I think maybe the spec should be changed to clarify existing restrictions or add more restrictions for cases when blocks are passed as function argument.

I am not sure that blocks in function call parameters contradict the rule of them being statically determinable though. You can statically determine what blocks are being passed into a function. There might be multiple different ones though. However, it should be possible to lower the function pointers into the direct calls.