This is an archive of the discontinued LLVM Phabricator instance.

[ABI] [C++20] [Modules] Don't generate vtable if the class is defined in other module unit
ClosedPublic

Authored by ChuanqiXu on May 6 2023, 1:30 AM.

Details

Summary

Close https://github.com/llvm/llvm-project/issues/61940.

The root cause is that clang will generate vtable as strong symbol now even if the corresponding class is defined in other module units. After I check the wording in Itanium ABI, I find this is not inconsistent. Itanium ABI 5.2.3 (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vague-vtable) says:

The virtual table for a class is emitted in the same object containing the definition of its key function, i.e. the first non-pure virtual function that is not inline at the point of class definition.

So the current behavior is incorrect. This patch tries to address this. Also I think we need to do a similar change for MSVC ABI. But I don't find the formal wording. So I don't address this in this patch.

Diff Detail

Event Timeline

ChuanqiXu created this revision.May 6 2023, 1:30 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 6 2023, 1:30 AM
ChuanqiXu requested review of this revision.May 6 2023, 1:30 AM
ChuanqiXu added inline comments.
clang/lib/CodeGen/CGVTables.cpp
1154–1155

The patch won't affect MSVC due to the check here.

dblaikie added inline comments.May 8 2023, 6:47 PM
clang/lib/CodeGen/CGVTables.cpp
1183–1186

Where's the code that "homes" the vtable into the module definition's object file? Is it possible to share these checks so they're more closely matched/harder to get out of sync?

ChuanqiXu added inline comments.May 8 2023, 8:11 PM
clang/lib/CodeGen/CGVTables.cpp
1183–1186

I think this function (isVTableExternal) is the proper place since other wordings of 5.2.3 are addressed by the function too. Or do I misunderstand your meanings?

ChuanqiXu updated this revision to Diff 524983.May 23 2023, 8:25 PM

Rebase with main.

This looks reasonable to me - but the vtable stuff is not an area that I am familiar with, so I'll defer to the other reviewers.

clang/lib/CodeGen/CGVTables.cpp
1181–1182

This just read a little awkwardly to me...

it is agreed that "another module unit" does include the specific case (but it also includes TUs from other modules). Really, this is a case where we just need to have a test that says "not in the current TU".

ChuanqiXu added inline comments.May 24 2023, 1:35 AM
clang/lib/CodeGen/CGVTables.cpp
1181–1182

The suggested comment looks not so correct. Since if the key function comes from another translation unit of a different module, the vtable should be defined there too.

Really, this is a case where we just need to have a test that says "not in the current TU".

I don't understand this a lot. Do you mean we need a new test to specify the case "not in the current TU"? (But I feel this is already covered.)

iains added inline comments.May 24 2023, 3:54 AM
clang/lib/CodeGen/CGVTables.cpp
1181–1182

The suggested comment looks not so correct. Since if the key function comes from another translation unit of a different module, the vtable should be defined there too.

Ah, OK, if that is a viable scenario, then my comment is wrong - it's not immediately obvious to me how we can have a class in one module with a key function in a different one (but probably i am missing some case).

dblaikie added inline comments.May 24 2023, 11:28 AM
clang/lib/CodeGen/CGVTables.cpp
1183–1186

Yeah, I'm trying to understand: Why were we getting a duplicate symbol before? I guess maybe there's some other code somewhere that's choosing the linkage of the vtable? And it's choosing strong linkage when the definition goes into the module?

ChuanqiXu added inline comments.May 24 2023, 7:37 PM
clang/lib/CodeGen/CGVTables.cpp
1183–1186

Yeah, understood. It looks better to answer the second question first:

I guess maybe there's some other code somewhere that's choosing the linkage of the vtable?

The code to decide the linkage of the vtable is in CodeGenModule::getVTableLinkage. The logic is simply, (let's skip some details for simplicity)
(1) If the linkage of the class is not externally visible, the linkage of the vtable should be internal;
(2) If the key function is inline, the linkage of the vtable is linkonce linkage.
(3) Otherwise it is strong linkage.

And it's choosing strong linkage when the definition goes into the module?

Yes, and this may be a correct behavior in the reproducers since the key functions are not inline.


Why were we getting a duplicate symbol before?

The fundamental reason was that we didn't care about names modules when we was generating the tables. Previously we decided if the key function lives in the current TU by searching its body in the current ASTContext. This was correct since both clang modules and pch don't have TU's semantics. But this changes after we introduce named modules.

See the reproducer: https://godbolt.org/z/jha8rYeoo. Both fmt.cppm and main.cpp contain the strong definition for vtable. It is good that the fmt.cppm contains a strong definition. But it is not good for main.cpp to contain the strong definition too. Since the key function doesn't live in the TU of main.cpp semantically. The compiler was just able to find its definition during the compilation. And this patch fixes this so that the linkage of vtable will be extern in main.cpp. So we avoid the problem.

ChuanqiXu added inline comments.May 24 2023, 7:48 PM
clang/lib/CodeGen/CGVTables.cpp
1183–1186

So my summary is:
(1) if the key function is inline, the linkage of the vtable is linkonce in every used TU.
(2) Otherwise the key function should only live in the TU that the key function lives in.

ChuanqiXu updated this revision to Diff 525422.May 24 2023, 8:34 PM

Oh, I got your points. I didn't handle the case that the key function lived in other module unit is inlined. This revision handles the case.

iains added a comment.Jun 12 2023, 1:04 PM

although I would welcome input from the ABI owners, testing shows that this patch does appear to DTRT and brings clang in line with the operation of GCC in this area. It would be good to land it (or decide what other action is needed) before 17 branches

Bueddl added a subscriber: Bueddl.Jun 13 2023, 1:17 AM

I had to go learn how modules work in order to review this; pity me.

clang/lib/CodeGen/CGVTables.cpp
1183–1186

Please avoid calling hasBody twice; if we might need the definition, we can just save it the first time.

We need to check specifically whether the key function *definition* is in another module unit, right? getCurrentKeyFunction will return the declaration from the class, and that might be from a different module unit even if the definition is not (maybe also vice-versa?).

You don't need the logic with AllowInlineKeyFunctions here: if we got an inline key function definition, we know the target allows inline key functions. So this can just be Def->isInAnotherModuleUnit() && !Def->isInlineSpecified().

Technically, I think we actually *could* emit inline definitions in module interface units as strong definitions, since there's a unique TU that we could make responsible for emitting them. But I don't think we should go down that road without Itanium ABI agreement; what we're doing here is more consistent with the current ABI.

On a more general point, I'm sorry the review of this patch was slow, but please continue to hold basic ABI questions up for code owner review; it's important not to be cavalier about these things.

ChuanqiXu updated this revision to Diff 531162.Jun 13 2023, 8:49 PM

Address comments.

On a more general point, I'm sorry the review of this patch was slow, but please continue to hold basic ABI questions up for code owner review; it's important not to be cavalier about these things.

Yeah. But it is good that we know the things are keeping going forward. Slowness is much better than stop.

clang/lib/CodeGen/CGVTables.cpp
1183–1186

We need to check specifically whether the key function *definition* is in another module unit, right?

Yes. Great catch. I've added the case to the tests.

Technically, I think we actually *could* emit inline definitions in module interface units as strong definitions, since there's a unique TU that we could make responsible for emitting them. But I don't think we should go down that road without Itanium ABI agreement; what we're doing here is more consistent with the current ABI.

Agreed. We can't do this now.

rjmccall accepted this revision.Jun 13 2023, 8:55 PM

Thanks, LGTM.

This revision is now accepted and ready to land.Jun 13 2023, 8:55 PM
This revision was landed with ongoing or failed builds.Jun 13 2023, 10:02 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptJun 13 2023, 10:02 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
thakis added a subscriber: thakis.Jun 14 2023, 8:35 AM

Looks like this breaks check-clang on Mac: http://45.33.8.238/macm1/62779/step_7.txt

Please take a look and revert for now if it takes a while to fix.

rZhBoYao added a subscriber: rZhBoYao.EditedJun 14 2023, 10:07 AM

Looks like this breaks check-clang on Mac: http://45.33.8.238/macm1/62779/step_7.txt

Same here. Kindly ping @ChuanqiXu .

Thanks for keeping the bot green. I see the failure comes from an ABI difference showed up in the test case. I'll try to address it and land it again later.