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

0 commit comments

Comments
 (0)