Skip to content

Commit 15bb026

Browse files
authored
[C++20] [Modules] [Itanium ABI] Generate the vtable in the module unit of dynamic classes (#75912)
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 ef18986 commit 15bb026

File tree

15 files changed

+183
-24
lines changed

15 files changed

+183
-24
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/include/clang/Serialization/ASTBitCodes.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,9 @@ enum ASTRecordTypes {
697697

698698
/// Record code for \#pragma clang unsafe_buffer_usage begin/end
699699
PP_UNSAFE_BUFFER_USAGE = 69,
700+
701+
/// Record code for vtables to emit.
702+
VTABLES_TO_EMIT = 70,
700703
};
701704

702705
/// Record types used within a source manager block.

clang/include/clang/Serialization/ASTReader.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -805,6 +805,11 @@ class ASTReader
805805
/// the consumer eagerly.
806806
SmallVector<GlobalDeclID, 16> EagerlyDeserializedDecls;
807807

808+
/// The IDs of all vtables to emit. The referenced declarations are passed
809+
/// to the consumers's HandleVTable eagerly after passing
810+
/// EagerlyDeserializedDecls.
811+
SmallVector<GlobalDeclID, 16> VTablesToEmit;
812+
808813
/// The IDs of all tentative definitions stored in the chain.
809814
///
810815
/// Sema keeps track of all tentative definitions in a TU because it has to
@@ -1514,6 +1519,7 @@ class ASTReader
15141519
bool isConsumerInterestedIn(Decl *D);
15151520
void PassInterestingDeclsToConsumer();
15161521
void PassInterestingDeclToConsumer(Decl *D);
1522+
void PassVTableToConsumer(CXXRecordDecl *RD);
15171523

15181524
void finishPendingActions();
15191525
void diagnoseOdrViolations();

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ class ASTWriter : public ASTDeserializationListener,
495495
std::vector<SourceRange> NonAffectingRanges;
496496
std::vector<SourceLocation::UIntTy> NonAffectingOffsetAdjustments;
497497

498+
/// A list of classes which need to emit the VTable in the corresponding
499+
/// object file.
500+
llvm::SmallVector<CXXRecordDecl *> PendingEmittingVTables;
501+
498502
/// Computes input files that didn't affect compilation of the current module,
499503
/// and initializes data structures necessary for leaving those files out
500504
/// during \c SourceManager serialization.
@@ -849,6 +853,8 @@ class ASTWriter : public ASTDeserializationListener,
849853

850854
bool getDoneWritingDeclsAndTypes() const { return DoneWritingDeclsAndTypes; }
851855

856+
void handleVTable(CXXRecordDecl *RD);
857+
852858
private:
853859
// ASTDeserializationListener implementation
854860
void ReaderInitialized(ASTReader *Reader) override;
@@ -943,6 +949,7 @@ class PCHGenerator : public SemaConsumer {
943949

944950
void InitializeSema(Sema &S) override { SemaPtr = &S; }
945951
void HandleTranslationUnit(ASTContext &Ctx) override;
952+
void HandleVTable(CXXRecordDecl *RD) override { Writer.handleVTable(RD); }
946953
ASTMutationListener *GetASTMutationListener() override;
947954
ASTDeserializationListener *GetASTDeserializationListener() override;
948955
bool hasEmittedPCH() const { return Buffer->IsComplete; }

clang/lib/AST/DeclBase.cpp

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

1131+
bool Decl::isInCurrentModuleUnit() const {
1132+
auto *M = getOwningModule();
1133+
1134+
if (!M || !M->isNamedModule())
1135+
return false;
1136+
1137+
return M == getASTContext().getCurrentNamedModule();
1138+
}
1139+
11311140
bool Decl::shouldEmitInExternalSource() const {
11321141
ExternalASTSource *Source = getASTContext().getExternalSource();
11331142
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/ItaniumCXXABI.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2130,6 +2130,9 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const {
21302130
if (!canSpeculativelyEmitVTableAsBaseClass(RD))
21312131
return false;
21322132

2133+
if (RD->shouldEmitInExternalSource())
2134+
return false;
2135+
21332136
// For a complete-object vtable (or more specifically, for the VTT), we need
21342137
// to be able to speculatively emit the vtables of all dynamic virtual bases.
21352138
for (const auto &B : RD->vbases()) {

clang/lib/Sema/SemaDecl.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18333,6 +18333,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
1833318333
if (NumInitMethods > 1 || !Def->hasInitMethod())
1833418334
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
1833518335
}
18336+
18337+
// If we're defining a dynamic class in a module interface unit, we always
18338+
// need to produce the vtable for it even if the vtable is not used in the
18339+
// current TU.
18340+
//
18341+
// The case that the current class is not dynamic is handled in
18342+
// MarkVTableUsed.
18343+
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
18344+
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
1833618345
}
1833718346

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

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18706,11 +18706,15 @@ bool Sema::DefineUsedVTables() {
1870618706

1870718707
bool DefineVTable = true;
1870818708

18709-
// If this class has a key function, but that key function is
18710-
// defined in another translation unit, we don't need to emit the
18711-
// vtable even though we're using it.
1871218709
const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class);
18713-
if (KeyFunction && !KeyFunction->hasBody()) {
18710+
// V-tables for non-template classes with an owning module are always
18711+
// uniquely emitted in that module.
18712+
if (Class->isInCurrentModuleUnit())
18713+
DefineVTable = true;
18714+
else if (KeyFunction && !KeyFunction->hasBody()) {
18715+
// If this class has a key function, but that key function is
18716+
// defined in another translation unit, we don't need to emit the
18717+
// vtable even though we're using it.
1871418718
// The key function is in another translation unit.
1871518719
DefineVTable = false;
1871618720
TemplateSpecializationKind TSK =
@@ -18755,7 +18759,7 @@ bool Sema::DefineUsedVTables() {
1875518759
DefinedAnything = true;
1875618760
MarkVirtualMembersReferenced(Loc, Class);
1875718761
CXXRecordDecl *Canonical = Class->getCanonicalDecl();
18758-
if (VTablesUsed[Canonical])
18762+
if (VTablesUsed[Canonical] && !Class->shouldEmitInExternalSource())
1875918763
Consumer.HandleVTable(Class);
1876018764

1876118765
// Warn if we're emitting a weak vtable. The vtable will be weak if there is

clang/lib/Serialization/ASTReader.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3903,6 +3903,13 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
39033903
}
39043904
break;
39053905

3906+
case VTABLES_TO_EMIT:
3907+
if (F.Kind == MK_MainFile ||
3908+
getContext().getLangOpts().BuildingPCHWithObjectFile)
3909+
for (unsigned I = 0, N = Record.size(); I != N;)
3910+
VTablesToEmit.push_back(getGlobalDeclID(F, LocalDeclID(Record[I++])));
3911+
break;
3912+
39063913
case IMPORTED_MODULES:
39073914
if (!F.isModule()) {
39083915
// If we aren't loading a module (which has its own exports), make
@@ -8067,6 +8074,10 @@ void ASTReader::PassInterestingDeclToConsumer(Decl *D) {
80678074
Consumer->HandleInterestingDecl(DeclGroupRef(D));
80688075
}
80698076

8077+
void ASTReader::PassVTableToConsumer(CXXRecordDecl *RD) {
8078+
Consumer->HandleVTable(RD);
8079+
}
8080+
80708081
void ASTReader::StartTranslationUnit(ASTConsumer *Consumer) {
80718082
this->Consumer = Consumer;
80728083

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4219,6 +4219,13 @@ void ASTReader::PassInterestingDeclsToConsumer() {
42194219

42204220
// If we add any new potential interesting decl in the last call, consume it.
42214221
ConsumingPotentialInterestingDecls();
4222+
4223+
for (GlobalDeclID ID : VTablesToEmit) {
4224+
auto *RD = cast<CXXRecordDecl>(GetDecl(ID));
4225+
assert(!RD->shouldEmitInExternalSource());
4226+
PassVTableToConsumer(RD);
4227+
}
4228+
VTablesToEmit.clear();
42224229
}
42234230

42244231
void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -927,6 +927,7 @@ void ASTWriter::WriteBlockInfoBlock() {
927927
RECORD(DECLS_TO_CHECK_FOR_DEFERRED_DIAGS);
928928
RECORD(PP_ASSUME_NONNULL_LOC);
929929
RECORD(PP_UNSAFE_BUFFER_USAGE);
930+
RECORD(VTABLES_TO_EMIT);
930931

931932
// SourceManager Block.
932933
BLOCK(SOURCE_MANAGER_BLOCK);
@@ -3957,6 +3958,10 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP,
39573958
Stream.EmitRecord(INTERESTING_IDENTIFIERS, InterestingIdents);
39583959
}
39593960

3961+
void ASTWriter::handleVTable(CXXRecordDecl *RD) {
3962+
PendingEmittingVTables.push_back(RD);
3963+
}
3964+
39603965
//===----------------------------------------------------------------------===//
39613966
// DeclContext's Name Lookup Table Serialization
39623967
//===----------------------------------------------------------------------===//
@@ -5141,6 +5146,13 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
51415146
// Write all of the DeclsToCheckForDeferredDiags.
51425147
for (auto *D : SemaRef.DeclsToCheckForDeferredDiags)
51435148
GetDeclRef(D);
5149+
5150+
// Write all classes need to emit the vtable definitions if required.
5151+
if (isWritingStdCXXNamedModules())
5152+
for (CXXRecordDecl *RD : PendingEmittingVTables)
5153+
GetDeclRef(RD);
5154+
else
5155+
PendingEmittingVTables.clear();
51445156
}
51455157

51465158
void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
@@ -5295,6 +5307,17 @@ void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
52955307
}
52965308
if (!DeleteExprsToAnalyze.empty())
52975309
Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);
5310+
5311+
RecordData VTablesToEmit;
5312+
for (CXXRecordDecl *RD : PendingEmittingVTables) {
5313+
if (!wasDeclEmitted(RD))
5314+
continue;
5315+
5316+
AddDeclRef(RD, VTablesToEmit);
5317+
}
5318+
5319+
if (!VTablesToEmit.empty())
5320+
Stream.EmitRecord(VTABLES_TO_EMIT, VTablesToEmit);
52985321
}
52995322

53005323
ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,

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

0 commit comments

Comments
 (0)