This is an archive of the discontinued LLVM Phabricator instance.

[Sema] Make sure the definition of a referenced virtual function is emitted when it is final
ClosedPublic

Authored by ahatanak on Jun 16 2017, 4:22 PM.

Details

Summary

The test case I added used to fail because of a linker error.

Linkage failed because Sema::MarkDeclRefReferenced would prevent the virtual method definition from being emitted by setting OdrUse=false and then code-gen would devirtualized the virtual call because its class is marked final.

This patch fixes the bug.

rdar://problem/27455779

Diff Detail

Event Timeline

ahatanak created this revision.Jun 16 2017, 4:22 PM
vsk edited edge metadata.EditedJun 16 2017, 5:51 PM
This comment has been deleted.
lib/Sema/SemaExpr.cpp
14715

Do you think it makes sense to eliminate all candidate virtual methods which can be devirtualized? If so, you could make "CanDevirtualizeMemberFunctionCall" a shared utility between Sema and CodeGen, and use it here. That function should give "the truth" about whether or not a call can be devirtualized.

14717

"MarkExprReferenced" has what looks like an incomplete version of "CanDevirtualizeMemberFunctionCall". Do you think there is an opportunity to share logic there as well?

test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
271

Have you looked into why "ptr->operator()();" compiles? We are either missing a devirtualization opportunity, or we have inconsistent logic for setting MightBeOdrUse for member calls. Either way, I think this patch is the right vehicle to address the issue.

ahatanak marked 2 inline comments as done.Jul 12 2017, 12:26 AM
ahatanak added inline comments.
lib/Sema/SemaExpr.cpp
14715

I moved CanDevirtualizeMemberFunctionCall to DeclCXX.cpp and added overloaded functions of canDevirtualizeCall and used one of them here. This caused changes in the generated IR in two test cases (no-devirt.cpp and vtable-available-externally.cpp). Both changes look correct to me.

test/CodeGenCXX/devirtualize-virtual-function-calls-final.cpp
271

"ptr->operator()();" creates a MemberExpr, which MarkMemberReferenced handles. The difference between MarkMemberReferenced and MarkDeclRefReferenced is that the former sets OdrUse to false when the method is pure while the latter does so when the method is virtual. Prior to r174341, MarkDeclRefReferenced was setting OdrUse to false for pure methods too (see r174242).

ahatanak updated this revision to Diff 106149.Jul 12 2017, 12:28 AM
ahatanak marked an inline comment as done.

Address review comments.

vsk added a comment.Jul 12 2017, 11:38 AM

I suggest changing the API for 'canDevirtualizeCall' a bit but this is looking great overall. The code movement and test case changes look good.

include/clang/AST/DeclCXX.h
1902 ↗(On Diff #106149)

Something like CXXMethodDecl *getDevirtualizedMethod(...) would fit in better with the current API. E.g, getCorrespondingMethodInClass is done this way (and returns nullptr when a method is not found).

lib/AST/DeclCXX.cpp
1693 ↗(On Diff #106149)

Instead of templating canDevirtualizeCall, you could do something like const_cast<CXXMethodDecl *>(this)->getDevirtualizedMethod(...).

ahatanak updated this revision to Diff 106347.Jul 12 2017, 4:40 PM
ahatanak marked 2 inline comments as done.

Define and use CXXMethodDecl::getDevirtualizedMethod, which returns the function that is called when a call is devirtualized.

vsk accepted this revision.Jul 12 2017, 4:42 PM

Thanks, this looks great.

This revision is now accepted and ready to land.Jul 12 2017, 4:42 PM
This revision was automatically updated to reflect the committed changes.