Skip to content

Commit 7a5c4cc

Browse files
committed
[C++20] [Modules] [Itanium ABI] Generate the vtable in the module unit
of dynamic classes Close #70585 and reflect itanium-cxx-abi/cxx-abi#170. The significant change of the patch is: for dynamic classes attached to module units, we generate the vtable to the attached module units directly and the key functions for such classes is meaningless.
1 parent 7a3b0cb commit 7a5c4cc

File tree

9 files changed

+139
-26
lines changed

9 files changed

+139
-26
lines changed

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,11 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
10461046
if (!RD->isExternallyVisible())
10471047
return llvm::GlobalVariable::InternalLinkage;
10481048

1049+
// V-tables for non-template classes with an owning module are always
1050+
// uniquely emitted in that module.
1051+
if (Module *M = RD->getOwningModule(); M && M->isNamedModule())
1052+
return llvm::GlobalVariable::ExternalLinkage;
1053+
10491054
// We're at the end of the translation unit, so the current key
10501055
// function is fully correct.
10511056
const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD);
@@ -1180,6 +1185,21 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
11801185
TSK == TSK_ExplicitInstantiationDefinition)
11811186
return false;
11821187

1188+
// Itanium C++ ABI [5.2.3]:
1189+
// Virtual tables for dynamic classes are emitted as follows:
1190+
//
1191+
// - If the class is templated, the tables are emitted in every object that
1192+
// references any of them.
1193+
// - Otherwise, if the class is attached to a module, the tables are uniquely
1194+
// emitted in the object for the module unit in which it is defined.
1195+
// - Otherwise, if the class has a key function (see below), the tables are
1196+
// emitted in the object for the translation unit containing the definition of
1197+
// the key function. This is unique if the key function is not inline.
1198+
// - Otherwise, the tables are emitted in every object that references any of
1199+
// them.
1200+
if (Module *M = RD->getOwningModule(); M && M->isNamedModule())
1201+
return M != CGM.getContext().getCurrentNamedModule();
1202+
11831203
// Otherwise, if the class doesn't have a key function (possibly
11841204
// anymore), the vtable must be defined here.
11851205
const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD);
@@ -1189,13 +1209,7 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
11891209
const FunctionDecl *Def;
11901210
// Otherwise, if we don't have a definition of the key function, the
11911211
// vtable must be defined somewhere else.
1192-
if (!keyFunction->hasBody(Def))
1193-
return true;
1194-
1195-
assert(Def && "The body of the key function is not assigned to Def?");
1196-
// If the non-inline key function comes from another module unit, the vtable
1197-
// must be defined there.
1198-
return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
1212+
return !keyFunction->hasBody(Def);
11991213
}
12001214

12011215
/// Given that we're currently at the end of the translation unit, and

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6738,6 +6738,15 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
67386738
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
67396739
DI->completeUnusedClass(*CRD);
67406740
}
6741+
// If we're emitting a dynamic class from the importable module we're
6742+
// emitting, we always need to emit the virtual table according to the ABI
6743+
// requirement.
6744+
if (CRD->getOwningModule() &&
6745+
CRD->getOwningModule()->isInterfaceOrPartition() &&
6746+
CRD->getDefinition() && CRD->isDynamicClass() &&
6747+
CRD->getOwningModule() == getContext().getCurrentNamedModule())
6748+
EmitVTable(CRD);
6749+
67416750
// Emit any static data members, they may be definitions.
67426751
for (auto *I : CRD->decls())
67436752
if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I))

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,6 +1801,12 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
18011801
if (VTable->hasInitializer())
18021802
return;
18031803

1804+
// If the class is attached to a C++ named module other than the one
1805+
// we're currently compiling, the vtable should be defined there.
1806+
if (Module *M = RD->getOwningModule();
1807+
M && M->isNamedModule() && M != CGM.getContext().getCurrentNamedModule())
1808+
return;
1809+
18041810
ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext();
18051811
const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
18061812
llvm::GlobalVariable::LinkageTypes Linkage = CGM.getVTableLinkage(RD);

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18127,6 +18127,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
1812718127
if (NumInitMethods > 1 || !Def->hasInitMethod())
1812818128
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
1812918129
}
18130+
18131+
// If we're defining a dynamic class in a module interface unit, we always
18132+
// need to produce the vtable for it even if the vtable is not used in the
18133+
// current TU.
18134+
//
18135+
// The case that the current class is not dynamic is handled in
18136+
// MarkVTableUsed.
18137+
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
18138+
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
1813018139
}
1813118140

1813218141
// Exit this scope of this tag's definition.

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18697,11 +18697,16 @@ bool Sema::DefineUsedVTables() {
1869718697

1869818698
bool DefineVTable = true;
1869918699

18700-
// If this class has a key function, but that key function is
18701-
// defined in another translation unit, we don't need to emit the
18702-
// vtable even though we're using it.
1870318700
const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class);
18704-
if (KeyFunction && !KeyFunction->hasBody()) {
18701+
// V-tables for non-template classes with an owning module are always
18702+
// uniquely emitted in that module.
18703+
if (Class->getOwningModule() &&
18704+
Class->getOwningModule()->isInterfaceOrPartition())
18705+
DefineVTable = true;
18706+
else if (KeyFunction && !KeyFunction->hasBody()) {
18707+
// If this class has a key function, but that key function is
18708+
// defined in another translation unit, we don't need to emit the
18709+
// vtable even though we're using it.
1870518710
// The key function is in another translation unit.
1870618711
DefineVTable = false;
1870718712
TemplateSpecializationKind TSK =

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2253,7 +2253,10 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
22532253

22542254
// Lazily load the key function to avoid deserializing every method so we can
22552255
// compute it.
2256-
if (WasDefinition) {
2256+
//
2257+
// The key function in named module is meaningless.
2258+
if (WasDefinition && (!D->getOwningModule() ||
2259+
!D->getOwningModule()->isInterfaceOrPartition())) {
22572260
DeclID KeyFn = readDeclID();
22582261
if (KeyFn && D->isCompleteDefinition())
22592262
// FIXME: This is wrong for the ARM ABI, where some other module may have
@@ -3212,6 +3215,14 @@ static bool isConsumerInterestedIn(ASTContext &Ctx, Decl *D, bool HasBody) {
32123215
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
32133216
return true;
32143217

3218+
// The dynamic class defined in a named module is interesting.
3219+
// The code generator needs to emit its vtable there.
3220+
if (const auto *Class = dyn_cast<CXXRecordDecl>(D))
3221+
return Class->getOwningModule() &&
3222+
Class->getOwningModule()->isInterfaceOrPartition() &&
3223+
Class->getOwningModule() == Ctx.getCurrentNamedModule() &&
3224+
Class->getDefinition() && Class->isDynamicClass();
3225+
32153226
return false;
32163227
}
32173228

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1483,10 +1483,15 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
14831483
if (D->isThisDeclarationADefinition())
14841484
Record.AddCXXDefinitionData(D);
14851485

1486-
// Store (what we currently believe to be) the key function to avoid
1487-
// deserializing every method so we can compute it.
1488-
if (D->isCompleteDefinition())
1489-
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
1486+
if (D->isCompleteDefinition()) {
1487+
if (D->getOwningModule() && D->getOwningModule()->isInterfaceOrPartition())
1488+
Writer.ModularCodegenDecls.push_back(Writer.GetDeclRef(D));
1489+
else {
1490+
// Store (what we currently believe to be) the key function to avoid
1491+
// deserializing every method so we can compute it.
1492+
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
1493+
}
1494+
}
14901495

14911496
Code = serialization::DECL_CXX_RECORD;
14921497
}

clang/test/CodeGenCXX/modules-vtable.cppm

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
// RUN: %t/M-A.cppm -o %t/M-A.pcm
2525
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=M:A=%t/M-A.pcm \
2626
// RUN: %t/M-B.cppm -emit-llvm -o - | FileCheck %t/M-B.cppm
27+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 \
28+
// RUN: %t/M-A.pcm -emit-llvm -o - | FileCheck %t/M-A.cppm
2729

2830
//--- Mod.cppm
2931
export module Mod;
@@ -41,9 +43,10 @@ Base::~Base() {}
4143
// CHECK: @_ZTSW3Mod4Base = constant
4244
// CHECK: @_ZTIW3Mod4Base = constant
4345

44-
// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
45-
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
46-
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
46+
// With the new Itanium C++ ABI, the linkage of vtables in modules don't need to be linkonce ODR.
47+
// CHECK-INLINE: @_ZTVW3Mod4Base = {{.*}}unnamed_addr constant
48+
// CHECK-INLINE: @_ZTSW3Mod4Base = {{.*}}constant
49+
// CHECK-INLINE: @_ZTIW3Mod4Base = {{.*}}constant
4750

4851
module :private;
4952
int private_use() {
@@ -60,11 +63,11 @@ int use() {
6063

6164
// CHECK-NOT: @_ZTSW3Mod4Base = constant
6265
// CHECK-NOT: @_ZTIW3Mod4Base = constant
63-
// CHECK: @_ZTVW3Mod4Base = external unnamed_addr
66+
// CHECK: @_ZTVW3Mod4Base = external
6467

65-
// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr {{.*}}unnamed_addr constant
66-
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr {{.*}}constant
67-
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr {{.*}}constant
68+
// CHECK-INLINE-NOT: @_ZTSW3Mod4Base = constant
69+
// CHECK-INLINE-NOT: @_ZTIW3Mod4Base = constant
70+
// CHECK-INLINE: @_ZTVW3Mod4Base = external
6871

6972
// Check the case that the declaration of the key function comes from another
7073
// module unit but the definition of the key function comes from the current
@@ -82,6 +85,10 @@ int a_use() {
8285
return 43;
8386
}
8487

88+
// CHECK: @_ZTVW1M1C = unnamed_addr constant
89+
// CHECK: @_ZTSW1M1C = constant
90+
// CHECK: @_ZTIW1M1C = constant
91+
8592
//--- M-B.cppm
8693
export module M:B;
8794
import :A;
@@ -93,6 +100,6 @@ int b_use() {
93100
return 43;
94101
}
95102

96-
// CHECK: @_ZTVW1M1C = unnamed_addr constant
97-
// CHECK: @_ZTSW1M1C = constant
98-
// CHECK: @_ZTIW1M1C = constant
103+
// CHECK: @_ZTVW1M1C = external
104+
// CHECK-NOT: @_ZTSW1M1C = constant
105+
// CHECK-NOT: @_ZTIW1M1C = constant

clang/test/CodeGenCXX/pr70585.cppm

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// REQUIRES: !system-windows
2+
3+
// RUN: rm -rf %t
4+
// RUN: split-file %s %t
5+
// RUN: cd %t
6+
//
7+
// RUN: %clang_cc1 -std=c++20 %t/layer1.cppm -triple %itanium_abi_triple \
8+
// RUN: -emit-module-interface -o %t/foo-layer1.pcm
9+
// RUN: %clang_cc1 -std=c++20 %t/layer2.cppm -triple %itanium_abi_triple \
10+
// RUN: -emit-module-interface -fmodule-file=foo:layer1=%t/foo-layer1.pcm \
11+
// RUN: -o %t/foo-layer2.pcm
12+
// RUN: %clang_cc1 -std=c++20 %t/foo-layer1.pcm -S -emit-llvm -o - | FileCheck %t/layer1.cppm
13+
// RUN: %clang_cc1 -std=c++20 %t/foo-layer2.pcm -S -emit-llvm -o - \
14+
// RUN: -fmodule-file=foo:layer1=%t/foo-layer1.pcm | FileCheck %t/layer2.cppm
15+
//
16+
// Check the case about emitting object files from sources directly.
17+
// RUN: %clang_cc1 -std=c++20 %t/layer1.cppm -triple %itanium_abi_triple \
18+
// RUN: -S -emit-llvm -o - | FileCheck %t/layer1.cppm
19+
// RUN: %clang_cc1 -std=c++20 %t/layer2.cppm -triple %itanium_abi_triple -S -emit-llvm \
20+
// RUN: -fmodule-file=foo:layer1=%t/foo-layer1.pcm -o - | FileCheck %t/layer2.cppm
21+
22+
//--- layer1.cppm
23+
export module foo:layer1;
24+
struct Fruit {
25+
virtual ~Fruit() = default;
26+
virtual void eval();
27+
};
28+
29+
// CHECK-DAG: @_ZTVW3foo5Fruit = unnamed_addr constant
30+
// CHECK-DAG: @_ZTSW3foo5Fruit = constant
31+
// CHECK-DAG: @_ZTIW3foo5Fruit = constant
32+
33+
// Testing that:
34+
// (1) The use of virtual functions won't produce the vtable.
35+
// (2) The definition of key functions won't produce the vtable.
36+
//
37+
//--- layer2.cppm
38+
export module foo:layer2;
39+
import :layer1;
40+
export void layer2_fun() {
41+
Fruit *b = new Fruit();
42+
b->eval();
43+
}
44+
void Fruit::eval() {}
45+
// CHECK: @_ZTVW3foo5Fruit = external unnamed_addr constant
46+
// CHECK-NOT: @_ZTSW3foo5Fruit
47+
// CHECK-NOT: @_ZTIW3foo5Fruit

0 commit comments

Comments
 (0)