This is an archive of the discontinued LLVM Phabricator instance.

[CodeGen] Check the cleanup flag before destructing lifetime-extended temporaries created in conditional expressions
AbandonedPublic

Authored by ahatanak on Jul 1 2020, 2:45 PM.

Details

Reviewers
rsmith
rjmccall
Summary

The lifetime-extended temporary of the true operand shouldn't be destructed when the false operand is evaluated and throws. Call pushFullExprCleanup instead of EHScopeStack::pushCleanup in pushLifetimeExtendedDestroy so that the destructor is called conditionally when we are in a conditional expression.

rdar://problem/64829372

Diff Detail

Event Timeline

ahatanak created this revision.Jul 1 2020, 2:45 PM

Please adjust the commit message to be clear that this is about lifetime-extended temporaries; it's not like we got this wrong for all temporaries.

Do we need to do anything to ensure that the after-full-expression cleanup is configured conditionally?

ahatanak retitled this revision from [CodeGen] Check the cleanup flag before destructing temporaries created in conditional expressions to [CodeGen] Check the cleanup flag before destructing lifetime-extended temporaries created in conditional expressions.Jul 2 2020, 1:10 PM
ahatanak edited the summary of this revision. (Show Details)

After-full-expression cleanup looks fine to me. pushCleanupAfterFullExpr sets the flags and saves the values when it's in a conditional branch.

I think ideally we shouldn't have to clean up anything in the true expression when the false expression throws, but I wasn't able to come up with an easy way to make IRGen avoid that.

After-full-expression cleanup looks fine to me. pushCleanupAfterFullExpr sets the flags and saves the values when it's in a conditional branch.

I think ideally we shouldn't have to clean up anything in the true expression when the false expression throws, but I wasn't able to come up with an easy way to make IRGen avoid that.

It's unavoidable; you can have code after the merge point that can throw, and you can only know dynamically which side you came from at that point.

In test case test13 in clang/test/CodeGenCXX/exceptions.cpp, I think you can turn invoke void @_ZN6test131AC1Ev into call void @_ZN6test131AC1Ev, no? If the false expression throws, there is nothing to clean up in the false expression and also nothing in the true expression has to be cleaned up.

And if the constructor call for A in the true expression throws, it's not necessary to call the destructor either since the object hasn't been constructed.

In test case test13 in clang/test/CodeGenCXX/exceptions.cpp, I think you can turn invoke void @_ZN6test131AC1Ev into call void @_ZN6test131AC1Ev, no? If the false expression throws, there is nothing to clean up in the false expression and also nothing in the true expression has to be cleaned up.

Yes, this is true. It would be possible to enhance Clang's cleanup stack to support this sort of thing — we'd want to be able to mark a cleanup as "currently known inactive" without potentially popping it off the cleanup stack, and then we could have conditional scopes remember the cleanups that were added, deactivate them this way, and then reactivate them after the merge. Swift's cleanup manager supports something similar.

In test case test13 in clang/test/CodeGenCXX/exceptions.cpp, I think you can turn invoke void @_ZN6test131AC1Ev into call void @_ZN6test131AC1Ev, no? If the false expression throws, there is nothing to clean up in the false expression and also nothing in the true expression has to be cleaned up.

Yes, this is true. It would be possible to enhance Clang's cleanup stack to support this sort of thing — we'd want to be able to mark a cleanup as "currently known inactive" without potentially popping it off the cleanup stack, and then we could have conditional scopes remember the cleanups that were added, deactivate them this way, and then reactivate them after the merge. Swift's cleanup manager supports something similar.

OK, I see. Since the enhancement isn't trivial to implement, I think we should do it in a separate patch if it has a large impact on code size. WDYT?

I agree, that can be done separately.