This is an archive of the discontinued LLVM Phabricator instance.

[CodeGen] Avoid destructing a struct type that has already been destructed by a delegated constructor
ClosedPublic

Authored by ahatanak on Apr 6 2018, 11:24 AM.

Details

Summary

This patch fixes a bug where a struct with an ObjC __weak field gets destructed after it has already been destructed. This bug was introduced in r328731, which made changes to the ABI that caused structs with ObjC pointer fields to be destructed in the callee in some cases.

This happens in two cases:

  1. C++11 inheriting constructors.
  2. When EmitConstructorBody does complete->base constructor delegation optimization.

I fixed the first case by making changes to canEmitDelegateCallArgs so that it returns false when the constructor has a parameter that is destructed in the callee.

For the second case, I made changes so that EmitParmDecl doesn't push the destructor cleanup for the struct parameter if the function is a constructor that is going to delegate to the base constructor. Alternatively, I think it's possible to just disable the optimization in EmitConstructorBody if canEmitDelegateCallArgs returns false.

rdar://problem/39194693

Diff Detail

Repository
rC Clang

Event Timeline

ahatanak created this revision.Apr 6 2018, 11:24 AM

Hmm. I'm not actually sure *why* it's not okay to forward callee-cleanup arguments. Do we just not have the necessary logic to disable the cleanup in the caller?

Hmm. I'm not actually sure *why* it's not okay to forward callee-cleanup arguments. Do we just not have the necessary logic to disable the cleanup in the caller?

It seems that it would be OK if there was a way to disable the cleanup in the caller when we know that we are delegating to another constructor, possibly by setting the DelegateCXXConstructorCall here too. But maybe there are other reasons for not doing so.

Perhaps Richard (who committed r274049) knows why we can't call the delegated constructor when there are callee-cleanup arguments?

If that's the problem, then I think the right design is for CallArg to include an optional cleanup reference for a cleanup that can be deactivated at the instant of the call (we should assert that this exists for parameters that are destroyed in the callee), and then for forwarding it's just a matter of getting that cleanup from parameter emission to forwarding-argument emission.

ahatanak updated this revision to Diff 142792.Apr 17 2018, 11:05 AM

Deactivate the cleanups for callee-destructed parameters that are being forwarded to a delegated constructor.

I also added a new member (CurrentCleanupStackDepth) to CodeGenFunction that tracks the stack depth of the most recent RunCleanupsScope. This is needed to prevent popping the cleanup at the top of the stack in DeactivateCleanupBlock if the cleanup doesn't belong to the current RunCleanupsScope. Without this, the current RunCleanupsScope's CleanupStackDepth can point to a cleanup that doesn't exist on the stack anymore (CleanupStackDepth.encloses(EHStack.stable_begin()) doesn't hold true anymore), which can cause CodeGenFunction::PopCleanupBlocks to pop all the cleanups before it hits the bottom of the stack and crash.

rjmccall added inline comments.Apr 21 2018, 5:49 AM
lib/CodeGen/CodeGenFunction.h
851

You don't need (or want) these accessors, I think; this is just private state of the CGF object, and nobody else should be using it.

1108

It's too bad that we need this DenseMap in every CGF when actually only a very specific set of thunk functions will actually use it. But I guess DenseMap is at least trivial to construct/destroy when empty, which will be the most common case.

1112

How about CurrentCleanupScopeDepth? The current name makes it sound like it's the active depth of the cleanup stack.

ahatanak updated this revision to Diff 143624.Apr 23 2018, 12:58 PM
ahatanak marked 2 inline comments as done.

Address review comments.

rjmccall added inline comments.Apr 23 2018, 1:41 PM
lib/CodeGen/CGCall.cpp
3069

I wonder if this is something we should be taking from the CGFunctionInfo instead. It does seem plausible that it could vary, e.g. according to the calling convention. But maybe that's something we can handle in a separate patch?

lib/CodeGen/CodeGenFunction.h
590

Please rename this variable to something like OldCleanupScopeDepth.

Hmm. I'm not actually sure *why* it's not okay to forward callee-cleanup arguments. Do we just not have the necessary logic to disable the cleanup in the caller?

It seems that it would be OK if there was a way to disable the cleanup in the caller when we know that we are delegating to another constructor, possibly by setting the DelegateCXXConstructorCall here too. But maybe there are other reasons for not doing so.

Perhaps Richard (who committed r274049) knows why we can't call the delegated constructor when there are callee-cleanup arguments?

I think the only reason was that we didn't have a good way to handle cleanup emission in the thunk (as @rjmccall points out, we do need parameter cleanups in the thunk, but we need to disable them at the point of the call to the inherited constructor). I cannot think of any correctness reason that prevents using a thunk for the callee-cleanup case.

x.

lib/CodeGen/CGCall.cpp
3069

I assume you are talking about the call to isParamDestroyedInCallee? If so, yes, I think we can discuss it in a separate patch. I have plans to clean up the way ParamDestroyedInCallee is handled in Sema and IRGen.

rjmccall added inline comments.Apr 23 2018, 3:17 PM
lib/CodeGen/CGCall.cpp
3069

I assume you are talking about the call to isParamDestroyedInCallee?

Yeah.

I assume you are talking about the call to isParamDestroyedInCallee? If so, yes, I think we can discuss it in a separate patch.

Okay, great, thanks.

ahatanak updated this revision to Diff 144231.Apr 26 2018, 3:52 PM
ahatanak marked an inline comment as done.

Rename variable to something OldCleanupScopeDepth.

rjmccall accepted this revision.Apr 26 2018, 6:32 PM

Thanks, LGTM.

This revision is now accepted and ready to land.Apr 26 2018, 6:32 PM
This revision was automatically updated to reflect the committed changes.