This is an archive of the discontinued LLVM Phabricator instance.

[CodeGen][ObjC] Make block copy/dispose helper function exception-safe.
ClosedPublic

Authored by ahatanak on Jul 23 2018, 10:04 PM.

Details

Summary

Block copy/dispose helper functions currently aren’t exception-safe. When an exception is thrown in a copy function, the function exits without destroying or releasing C++ objects, ObjC objects, and __block objects that have already been copied. Similarly, dispose functions don't release ObjC objects and __block objects that are yet to be released when an exception is thrown.

Diff Detail

Repository
rC Clang

Event Timeline

ahatanak created this revision.Jul 23 2018, 10:04 PM

Note that in order to destruct C++ objects, I'm using pushDestroy which pushes a NormalCleanup when exception handling isn't enabled. This is different from PushDestructorCleanup which always pushes a NormalAndEHCleanup, but I think using pushDestroy is more correct since I don't think we need to do a cleanup on the EH path when exception isn't enabled.

Note that in order to destruct C++ objects, I'm using pushDestroy which pushes a NormalCleanup when exception handling isn't enabled. This is different from PushDestructorCleanup which always pushes a NormalAndEHCleanup, but I think using pushDestroy is more correct since I don't think we need to do a cleanup on the EH path when exception isn't enabled.

I don't think it makes any difference because we shouldn't be emitting cleanup paths when exceptions are disabled. I don't think there's an intended difference in semantics between those two destructor-cleanup paths, at least.

lib/CodeGen/CGBlocks.cpp
1603

There's already a CallBlockRelease cleanup that can presumably be unified with this.

1918

These two switches differ only in (1) what kind of cleanup they push and (2) a small optimization that can easily be conditionalized by the request to push an EH-only cleanup.

ahatanak updated this revision to Diff 157095.Jul 24 2018, 11:59 AM
ahatanak marked 2 inline comments as done.

Address review comments.

I think I should be able to merge pushBlockObjectRelease and enterByrefCleanup, but the BlockFieldFlags that are passed are different. We set BLOCK_FIELD_IS_WEAK in addition to BLOCK_FIELD_IS_BYREF when isObjCGCWeak() returns true and we are emitting the copy/dispose helper functions, but when enterByrefCleanup is called from EmitAutoVarCleanups, only BLOCK_FIELD_IS_BYREF is set.

I don't think it makes any difference because we shouldn't be emitting cleanup paths when exceptions are disabled. I don't think there's an intended difference in semantics between those two destructor-cleanup paths, at least.

OK, I see.

If it doesn't make any difference, I think I can push NormalAndEHCleanup unconditionally when copying a BlockObject capture instead of pushing NormalCleanup when EH is disabled. I made that change in the updated patch.

lib/CodeGen/CGBlocks.cpp
1918

Merged the two switch statements into helper function pushCaptureCleanup.

Address review comments.

I think I should be able to merge pushBlockObjectRelease and enterByrefCleanup, but the BlockFieldFlags that are passed are different. We set BLOCK_FIELD_IS_WEAK in addition to BLOCK_FIELD_IS_BYREF when isObjCGCWeak() returns true and we are emitting the copy/dispose helper functions, but when enterByrefCleanup is called from EmitAutoVarCleanups, only BLOCK_FIELD_IS_BYREF is set.

Heh, okay. It looks like the runtime doesn't do anything different, but I think it's probably more correct to always pass BLOCK_FIELD_IS_WEAK when destroying a __weak block in GC modes.

I don't think it makes any difference because we shouldn't be emitting cleanup paths when exceptions are disabled. I don't think there's an intended difference in semantics between those two destructor-cleanup paths, at least.

OK, I see.

If it doesn't make any difference, I think I can push NormalAndEHCleanup unconditionally when copying a BlockObject capture instead of pushing NormalCleanup when EH is disabled. I made that change in the updated patch.

Ok. Thanks, I like how it looks now.

ahatanak updated this revision to Diff 157149.Jul 24 2018, 3:49 PM

Merge pushBlockObjectRelease and enterByrefCleanup.

Heh, okay. It looks like the runtime doesn't do anything different, but I think it's probably more correct to always pass BLOCK_FIELD_IS_WEAK when destroying a __weak block in GC modes.

I tried passing BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF to the call to enterByrefCleanup in EmitAutoVarCleanups, but it looks like test/CodeGenObjC/blocks.m fails if I do so. The test was committed in r125823 and there is a comment that says " We're not supposed to pass BLOCK_FIELD_IS_WEAK here".

http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110214/038996.html

Should we change the check in the test?

Merge pushBlockObjectRelease and enterByrefCleanup.

Heh, okay. It looks like the runtime doesn't do anything different, but I think it's probably more correct to always pass BLOCK_FIELD_IS_WEAK when destroying a __weak block in GC modes.

I tried passing BLOCK_FIELD_IS_WEAK | BLOCK_FIELD_IS_BYREF to the call to enterByrefCleanup in EmitAutoVarCleanups, but it looks like test/CodeGenObjC/blocks.m fails if I do so. The test was committed in r125823 and there is a comment that says " We're not supposed to pass BLOCK_FIELD_IS_WEAK here".

http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110214/038996.html

Should we change the check in the test?

Thanks for the archaeology; that's a very good question. It looks to me like that comment was unrelated to the functionality change made by that patch, and nothing in the radar history explains it, either. I don't see anything in the runtime source which suggests that it's wrong to pass BYREF|WEAK to Block_object_dispose. Going way back, it does seem to have been necessary under GC to pass BYREF|WEAK for Block_object_assign, but the runtime never did anything special with the flag for Block_object_dispose; it just always treated it exactly the same as BYREF alone.

I think it's fine to just change the test.

ahatanak updated this revision to Diff 157380.Jul 25 2018, 3:32 PM

Set the BLOCK_FIELD_IS_WEAK bit of the flag passed to the call to enterByrefCleanup in EmitAutoVarCleanups and fix test case test/CodeGenObjC/blocks.m.

rjmccall added inline comments.Jul 25 2018, 3:56 PM
lib/CodeGen/CGBlocks.cpp
2578–2579

Please document the meaning of the parameters here.

ahatanak updated this revision to Diff 157395.Jul 25 2018, 4:36 PM
ahatanak marked an inline comment as done.

Document the meaning of enterByrefCleanup's parameters.

rjmccall accepted this revision.Jul 25 2018, 7:54 PM

Thanks, LGTM.

This revision is now accepted and ready to land.Jul 25 2018, 7:54 PM
ahatanak added inline comments.Jul 25 2018, 10:15 PM
lib/CodeGen/CGBlocks.cpp
2578–2579

One more question: I think documentation comments for public APIs are normally in the header file. Should we move these comments to CodeGenFunction.h?

rjmccall added inline comments.Jul 25 2018, 11:23 PM
lib/CodeGen/CGBlocks.cpp
2578–2579

It'd be better, yeah. Having it be on a unique implementation is also okay because it's not hard to find, but yeah, header is better.

This revision was automatically updated to reflect the committed changes.