Skip to content

Commit cf8be3c

Browse files
committed
[C++20] [Modules] [Itanium ABI] Generate the vtable in the module unit
of dynamic classes Close llvm#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 99873b3 commit cf8be3c

File tree

11 files changed

+138
-23
lines changed

11 files changed

+138
-23
lines changed

clang/include/clang/AST/DeclBase.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,9 @@ class alignas(8) Decl {
670670
/// Whether this declaration comes from another module unit.
671671
bool isInAnotherModuleUnit() const;
672672

673+
/// Whether this declaration comes from the same module unit being compiled.
674+
bool isInCurrentModuleUnit() const;
675+
673676
/// Whether the definition of the declaration should be emitted in external
674677
/// sources.
675678
bool shouldEmitInExternalSource() const;

clang/lib/AST/DeclBase.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,15 @@ bool Decl::isInAnotherModuleUnit() const {
11061106
return M != getASTContext().getCurrentNamedModule();
11071107
}
11081108

1109+
bool Decl::isInCurrentModuleUnit() const {
1110+
auto *M = getOwningModule();
1111+
1112+
if (!M || !M->isNamedModule())
1113+
return false;
1114+
1115+
return M == getASTContext().getCurrentNamedModule();
1116+
}
1117+
11091118
bool Decl::shouldEmitInExternalSource() const {
11101119
ExternalASTSource *Source = getASTContext().getExternalSource();
11111120
if (!Source)

clang/lib/CodeGen/CGVTables.cpp

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

1054+
// V-tables for non-template classes with an owning module are always
1055+
// uniquely emitted in that module.
1056+
if (RD->isInNamedModule())
1057+
return llvm::GlobalVariable::ExternalLinkage;
1058+
10541059
// We're at the end of the translation unit, so the current key
10551060
// function is fully correct.
10561061
const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD);
@@ -1185,6 +1190,21 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
11851190
TSK == TSK_ExplicitInstantiationDefinition)
11861191
return false;
11871192

1193+
// Itanium C++ ABI [5.2.3]:
1194+
// Virtual tables for dynamic classes are emitted as follows:
1195+
//
1196+
// - If the class is templated, the tables are emitted in every object that
1197+
// references any of them.
1198+
// - Otherwise, if the class is attached to a module, the tables are uniquely
1199+
// emitted in the object for the module unit in which it is defined.
1200+
// - Otherwise, if the class has a key function (see below), the tables are
1201+
// emitted in the object for the translation unit containing the definition of
1202+
// the key function. This is unique if the key function is not inline.
1203+
// - Otherwise, the tables are emitted in every object that references any of
1204+
// them.
1205+
if (RD->isInNamedModule())
1206+
return RD->shouldEmitInExternalSource();
1207+
11881208
// Otherwise, if the class doesn't have a key function (possibly
11891209
// anymore), the vtable must be defined here.
11901210
const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD);
@@ -1194,13 +1214,7 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
11941214
const FunctionDecl *Def;
11951215
// Otherwise, if we don't have a definition of the key function, the
11961216
// vtable must be defined somewhere else.
1197-
if (!keyFunction->hasBody(Def))
1198-
return true;
1199-
1200-
assert(Def && "The body of the key function is not assigned to Def?");
1201-
// If the non-inline key function comes from another module unit, the vtable
1202-
// must be defined there.
1203-
return Def->shouldEmitInExternalSource() && !Def->isInlineSpecified();
1217+
return !keyFunction->hasBody(Def);
12041218
}
12051219

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

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6853,6 +6853,13 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) {
68536853
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
68546854
DI->completeUnusedClass(*CRD);
68556855
}
6856+
// If we're emitting a dynamic class from the importable module we're
6857+
// emitting, we always need to emit the virtual table according to the ABI
6858+
// requirement.
6859+
if (CRD->getDefinition() && CRD->isDynamicClass() &&
6860+
CRD->isInCurrentModuleUnit())
6861+
EmitVTable(CRD);
6862+
68566863
// Emit any static data members, they may be definitions.
68576864
for (auto *I : CRD->decls())
68586865
if (isa<VarDecl>(I) || isa<CXXRecordDecl>(I))

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,6 +1830,9 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
18301830
if (VTable->hasInitializer())
18311831
return;
18321832

1833+
if (RD->shouldEmitInExternalSource())
1834+
return;
1835+
18331836
ItaniumVTableContext &VTContext = CGM.getItaniumVTableContext();
18341837
const VTableLayout &VTLayout = VTContext.getVTableLayout(RD);
18351838
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
@@ -18327,6 +18327,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
1832718327
if (NumInitMethods > 1 || !Def->hasInitMethod())
1832818328
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
1832918329
}
18330+
18331+
// If we're defining a dynamic class in a module interface unit, we always
18332+
// need to produce the vtable for it even if the vtable is not used in the
18333+
// current TU.
18334+
//
18335+
// The case that the current class is not dynamic is handled in
18336+
// MarkVTableUsed.
18337+
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
18338+
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
1833018339
}
1833118340

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

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18684,11 +18684,15 @@ bool Sema::DefineUsedVTables() {
1868418684

1868518685
bool DefineVTable = true;
1868618686

18687-
// If this class has a key function, but that key function is
18688-
// defined in another translation unit, we don't need to emit the
18689-
// vtable even though we're using it.
1869018687
const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class);
18691-
if (KeyFunction && !KeyFunction->hasBody()) {
18688+
// V-tables for non-template classes with an owning module are always
18689+
// uniquely emitted in that module.
18690+
if (Class->isInNamedModule())
18691+
DefineVTable = true;
18692+
else if (KeyFunction && !KeyFunction->hasBody()) {
18693+
// If this class has a key function, but that key function is
18694+
// defined in another translation unit, we don't need to emit the
18695+
// vtable even though we're using it.
1869218696
// The key function is in another translation unit.
1869318697
DefineVTable = false;
1869418698
TemplateSpecializationKind TSK =

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,6 +3239,12 @@ bool ASTReader::isConsumerInterestedIn(Decl *D) {
32393239
if (ES->hasExternalDefinitions(D) == ExternalASTSource::EK_Never)
32403240
return true;
32413241

3242+
// The dynamic class defined in a named module is interesting.
3243+
// The code generator needs to emit its vtable there.
3244+
if (const auto *Class = dyn_cast<CXXRecordDecl>(D))
3245+
return Class->isInCurrentModuleUnit() &&
3246+
Class->getDefinition() && Class->isDynamicClass();
3247+
32423248
return false;
32433249
}
32443250

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,8 +1537,14 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
15371537
if (D->isThisDeclarationADefinition())
15381538
Record.AddCXXDefinitionData(D);
15391539

1540+
if (D->isCompleteDefinition() && D->isInNamedModule())
1541+
Writer.AddDeclRef(D, Writer.ModularCodegenDecls);
1542+
15401543
// Store (what we currently believe to be) the key function to avoid
15411544
// deserializing every method so we can compute it.
1545+
//
1546+
// FIXME: Avoid adding the key function if the class is defined in
1547+
// module purview since the key function is meaningless in module purview.
15421548
if (D->isCompleteDefinition())
15431549
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
15441550

clang/test/CodeGenCXX/modules-vtable.cppm

Lines changed: 19 additions & 12 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() {
@@ -58,13 +61,13 @@ int use() {
5861
return 43;
5962
}
6063

61-
// CHECK-NOT: @_ZTSW3Mod4Base = constant
62-
// CHECK-NOT: @_ZTIW3Mod4Base = constant
63-
// CHECK: @_ZTVW3Mod4Base = external unnamed_addr
64+
// CHECK-NOT: @_ZTSW3Mod4Base
65+
// CHECK-NOT: @_ZTIW3Mod4Base
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
69+
// CHECK-INLINE-NOT: @_ZTIW3Mod4Base
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
105+
// CHECK-NOT: @_ZTIW1M1C

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 -emit-llvm -o - | FileCheck %t/layer1.cppm
13+
// RUN: %clang_cc1 -std=c++20 %t/foo-layer2.pcm -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: -emit-llvm -o - | FileCheck %t/layer1.cppm
19+
// RUN: %clang_cc1 -std=c++20 %t/layer2.cppm -triple %itanium_abi_triple -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)