Skip to content

Commit 7399d24

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 912e2c4 commit 7399d24

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
@@ -6777,6 +6777,15 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
67776777
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
67786778
DI->completeUnusedClass(*CRD);
67796779
}
6780+
// If we're emitting a dynamic class from the importable module we're
6781+
// emitting, we always need to emit the virtual table according to the ABI
6782+
// requirement.
6783+
if (CRD->getOwningModule() &&
6784+
CRD->getOwningModule()->isInterfaceOrPartition() &&
6785+
CRD->getDefinition() && CRD->isDynamicClass() &&
6786+
CRD->getOwningModule() == getContext().getCurrentNamedModule())
6787+
EmitVTable(CRD);
6788+
67806789
// Emit any static data members, they may be definitions.
67816790
for (auto *I : CRD->decls())
67826791
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
@@ -1802,6 +1802,12 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
18021802
if (VTable->hasInitializer())
18031803
return;
18041804

1805+
// If the class is attached to a C++ named module other than the one
1806+
// we're currently compiling, the vtable should be defined there.
1807+
if (Module *M = RD->getOwningModule();
1808+
M && M->isNamedModule() && M != CGM.getContext().getCurrentNamedModule())
1809+
return;
1810+
18051811
ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext();
18061812
const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
18071813
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
@@ -18441,6 +18441,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
1844118441
if (NumInitMethods > 1 || !Def->hasInitMethod())
1844218442
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
1844318443
}
18444+
18445+
// If we're defining a dynamic class in a module interface unit, we always
18446+
// need to produce the vtable for it even if the vtable is not used in the
18447+
// current TU.
18448+
//
18449+
// The case that the current class is not dynamic is handled in
18450+
// MarkVTableUsed.
18451+
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
18452+
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
1844418453
}
1844518454

1844618455
// 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
@@ -18674,11 +18674,16 @@ bool Sema::DefineUsedVTables() {
1867418674

1867518675
bool DefineVTable = true;
1867618676

18677-
// If this class has a key function, but that key function is
18678-
// defined in another translation unit, we don't need to emit the
18679-
// vtable even though we're using it.
1868018677
const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class);
18681-
if (KeyFunction && !KeyFunction->hasBody()) {
18678+
// V-tables for non-template classes with an owning module are always
18679+
// uniquely emitted in that module.
18680+
if (Class->getOwningModule() &&
18681+
Class->getOwningModule()->isInterfaceOrPartition())
18682+
DefineVTable = true;
18683+
else if (KeyFunction && !KeyFunction->hasBody()) {
18684+
// If this class has a key function, but that key function is
18685+
// defined in another translation unit, we don't need to emit the
18686+
// vtable even though we're using it.
1868218687
// The key function is in another translation unit.
1868318688
DefineVTable = false;
1868418689
TemplateSpecializationKind TSK =

clang/lib/Serialization/ASTReaderDecl.cpp

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

22752275
// Lazily load the key function to avoid deserializing every method so we can
22762276
// compute it.
2277-
if (WasDefinition) {
2277+
//
2278+
// The key function in named module is meaningless.
2279+
if (WasDefinition && (!D->getOwningModule() ||
2280+
!D->getOwningModule()->isInterfaceOrPartition())) {
22782281
DeclID KeyFn = readDeclID();
22792282
if (KeyFn && D->isCompleteDefinition())
22802283
// FIXME: This is wrong for the ARM ABI, where some other module may have
@@ -3235,6 +3238,14 @@ static bool isConsumerInterestedIn(ASTContext &Ctx, Decl *D, bool HasBody) {
32353238
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
32363239
return true;
32373240

3241+
// The dynamic class defined in a named module is interesting.
3242+
// The code generator needs to emit its vtable there.
3243+
if (const auto *Class = dyn_cast<CXXRecordDecl>(D))
3244+
return Class->getOwningModule() &&
3245+
Class->getOwningModule()->isInterfaceOrPartition() &&
3246+
Class->getOwningModule() == Ctx.getCurrentNamedModule() &&
3247+
Class->getDefinition() && Class->isDynamicClass();
3248+
32383249
return false;
32393250
}
32403251

clang/lib/Serialization/ASTWriterDecl.cpp

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

1532-
// Store (what we currently believe to be) the key function to avoid
1533-
// deserializing every method so we can compute it.
1534-
if (D->isCompleteDefinition())
1535-
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
1532+
if (D->isCompleteDefinition()) {
1533+
if (D->getOwningModule() && D->getOwningModule()->isInterfaceOrPartition())
1534+
Writer.ModularCodegenDecls.push_back(Writer.GetDeclRef(D));
1535+
else {
1536+
// Store (what we currently believe to be) the key function to avoid
1537+
// deserializing every method so we can compute it.
1538+
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
1539+
}
1540+
}
15361541

15371542
Code = serialization::DECL_CXX_RECORD;
15381543
}

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)