Skip to content

Commit 847f9cb

Browse files
authored
Reland [C++20] [Modules] [Itanium ABI] Generate the vtable in the mod… (#102287)
Reland #75912 The differences of this PR between #75912 are: - Fixed a regression in `Decl::isInAnotherModuleUnit()` in DeclBase.cpp pointed by @mizvekov and add the corresponding test. - Fixed the regression in windows #97447. The changes are in `CodeGenModule::getVTableLinkage` from `clang/lib/CodeGen/CGVTables.cpp`. According to the feedbacks from MSVC devs, the linkage of vtables won't affected by modules. So I simply skipped the case for MSVC. Given this is more or less fundamental to the use of modules. I hope we can backport this to 19.x.
1 parent 65b4a77 commit 847f9cb

19 files changed

+374
-55
lines changed

clang/include/clang/AST/DeclBase.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,13 @@ 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+
676+
/// Whether the definition of the declaration should be emitted in external
677+
/// sources.
678+
bool shouldEmitInExternalSource() const;
679+
673680
/// Whether this declaration comes from explicit global module.
674681
bool isFromExplicitGlobalModule() const;
675682

clang/include/clang/Serialization/ASTBitCodes.h

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

722722
/// Record code for \#pragma clang unsafe_buffer_usage begin/end
723723
PP_UNSAFE_BUFFER_USAGE = 69,
724+
725+
/// Record code for vtables to emit.
726+
VTABLES_TO_EMIT = 70,
724727
};
725728

726729
/// 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
@@ -790,6 +790,11 @@ class ASTReader
790790
/// the consumer eagerly.
791791
SmallVector<GlobalDeclID, 16> EagerlyDeserializedDecls;
792792

793+
/// The IDs of all vtables to emit. The referenced declarations are passed
794+
/// to the consumers' HandleVTable eagerly after passing
795+
/// EagerlyDeserializedDecls.
796+
SmallVector<GlobalDeclID, 16> VTablesToEmit;
797+
793798
/// The IDs of all tentative definitions stored in the chain.
794799
///
795800
/// Sema keeps track of all tentative definitions in a TU because it has to
@@ -1500,6 +1505,7 @@ class ASTReader
15001505
bool isConsumerInterestedIn(Decl *D);
15011506
void PassInterestingDeclsToConsumer();
15021507
void PassInterestingDeclToConsumer(Decl *D);
1508+
void PassVTableToConsumer(CXXRecordDecl *RD);
15031509

15041510
void finishPendingActions();
15051511
void diagnoseOdrViolations();

clang/include/clang/Serialization/ASTWriter.h

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

503+
/// A list of classes which need to emit the VTable in the corresponding
504+
/// object file.
505+
llvm::SmallVector<CXXRecordDecl *> PendingEmittingVTables;
506+
503507
/// Computes input files that didn't affect compilation of the current module,
504508
/// and initializes data structures necessary for leaving those files out
505509
/// during \c SourceManager serialization.
@@ -857,6 +861,8 @@ class ASTWriter : public ASTDeserializationListener,
857861
return PredefinedDecls.count(D);
858862
}
859863

864+
void handleVTable(CXXRecordDecl *RD);
865+
860866
private:
861867
// ASTDeserializationListener implementation
862868
void ReaderInitialized(ASTReader *Reader) override;
@@ -951,6 +957,7 @@ class PCHGenerator : public SemaConsumer {
951957

952958
void InitializeSema(Sema &S) override { SemaPtr = &S; }
953959
void HandleTranslationUnit(ASTContext &Ctx) override;
960+
void HandleVTable(CXXRecordDecl *RD) override { Writer.handleVTable(RD); }
954961
ASTMutationListener *GetASTMutationListener() override;
955962
ASTDeserializationListener *GetASTDeserializationListener() override;
956963
bool hasEmittedPCH() const { return Buffer->IsComplete; }

clang/lib/AST/ASTContext.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12431,8 +12431,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
1243112431
!isMSStaticDataMemberInlineDefinition(VD))
1243212432
return false;
1243312433

12434-
// Variables in other module units shouldn't be forced to be emitted.
12435-
if (VD->isInAnotherModuleUnit())
12434+
if (VD->shouldEmitInExternalSource())
1243612435
return false;
1243712436

1243812437
// Variables that can be needed in other TUs are required.

clang/lib/AST/DeclBase.cpp

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,20 +1124,36 @@ bool Decl::isInAnotherModuleUnit() const {
11241124
if (!M)
11251125
return false;
11261126

1127+
// FIXME or NOTE: maybe we need to be clear about the semantics
1128+
// of clang header modules. e.g., if this lives in a clang header
1129+
// module included by the current unit, should we return false
1130+
// here?
1131+
//
1132+
// This is clear for header units as the specification says the
1133+
// header units live in a synthesised translation unit. So we
1134+
// can return false here.
11271135
M = M->getTopLevelModule();
1128-
// FIXME: It is problematic if the header module lives in another module
1129-
// unit. Consider to fix this by techniques like
1130-
// ExternalASTSource::hasExternalDefinitions.
1131-
if (M->isHeaderLikeModule())
1136+
if (!M->isNamedModule())
11321137
return false;
11331138

1134-
// A global module without parent implies that we're parsing the global
1135-
// module. So it can't be in another module unit.
1136-
if (M->isGlobalModule())
1139+
return M != getASTContext().getCurrentNamedModule();
1140+
}
1141+
1142+
bool Decl::isInCurrentModuleUnit() const {
1143+
auto *M = getOwningModule();
1144+
1145+
if (!M || !M->isNamedModule())
11371146
return false;
11381147

1139-
assert(M->isNamedModule() && "New module kind?");
1140-
return M != getASTContext().getCurrentNamedModule();
1148+
return M == getASTContext().getCurrentNamedModule();
1149+
}
1150+
1151+
bool Decl::shouldEmitInExternalSource() const {
1152+
ExternalASTSource *Source = getASTContext().getExternalSource();
1153+
if (!Source)
1154+
return false;
1155+
1156+
return Source->hasExternalDefinitions(this) == ExternalASTSource::EK_Always;
11411157
}
11421158

11431159
bool Decl::isFromExplicitGlobalModule() const {

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,29 +1081,41 @@ llvm::GlobalVariable::LinkageTypes
10811081
CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
10821082
if (!RD->isExternallyVisible())
10831083
return llvm::GlobalVariable::InternalLinkage;
1084-
1085-
// We're at the end of the translation unit, so the current key
1086-
// function is fully correct.
1087-
const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD);
1088-
if (keyFunction && !RD->hasAttr<DLLImportAttr>()) {
1084+
1085+
// In windows, the linkage of vtable is not related to modules.
1086+
bool IsInNamedModule = !getTarget().getCXXABI().isMicrosoft() &&
1087+
RD->isInNamedModule();
1088+
// If the CXXRecordDecl is not in a module unit, we need to get
1089+
// its key function. We're at the end of the translation unit, so the current
1090+
// key function is fully correct.
1091+
const CXXMethodDecl *keyFunction =
1092+
IsInNamedModule ? nullptr : Context.getCurrentKeyFunction(RD);
1093+
if (IsInNamedModule || (keyFunction && !RD->hasAttr<DLLImportAttr>())) {
10891094
// If this class has a key function, use that to determine the
10901095
// linkage of the vtable.
10911096
const FunctionDecl *def = nullptr;
1092-
if (keyFunction->hasBody(def))
1097+
if (keyFunction && keyFunction->hasBody(def))
10931098
keyFunction = cast<CXXMethodDecl>(def);
10941099

1095-
switch (keyFunction->getTemplateSpecializationKind()) {
1096-
case TSK_Undeclared:
1097-
case TSK_ExplicitSpecialization:
1100+
bool IsExternalDefinition =
1101+
IsInNamedModule ? RD->shouldEmitInExternalSource() : !def;
1102+
1103+
TemplateSpecializationKind Kind =
1104+
IsInNamedModule ? RD->getTemplateSpecializationKind()
1105+
: keyFunction->getTemplateSpecializationKind();
1106+
1107+
switch (Kind) {
1108+
case TSK_Undeclared:
1109+
case TSK_ExplicitSpecialization:
10981110
assert(
1099-
(def || CodeGenOpts.OptimizationLevel > 0 ||
1111+
(IsInNamedModule || def || CodeGenOpts.OptimizationLevel > 0 ||
11001112
CodeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&
1101-
"Shouldn't query vtable linkage without key function, "
1102-
"optimizations, or debug info");
1103-
if (!def && CodeGenOpts.OptimizationLevel > 0)
1113+
"Shouldn't query vtable linkage without the class in module units, "
1114+
"key function, optimizations, or debug info");
1115+
if (IsExternalDefinition && CodeGenOpts.OptimizationLevel > 0)
11041116
return llvm::GlobalVariable::AvailableExternallyLinkage;
11051117

1106-
if (keyFunction->isInlined())
1118+
if (keyFunction && keyFunction->isInlined())
11071119
return !Context.getLangOpts().AppleKext
11081120
? llvm::GlobalVariable::LinkOnceODRLinkage
11091121
: llvm::Function::InternalLinkage;
@@ -1122,7 +1134,7 @@ CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
11221134

11231135
case TSK_ExplicitInstantiationDeclaration:
11241136
llvm_unreachable("Should not have been asked to emit this");
1125-
}
1137+
}
11261138
}
11271139

11281140
// -fapple-kext mode does not support weak linkage, so we must use
@@ -1216,22 +1228,20 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
12161228
TSK == TSK_ExplicitInstantiationDefinition)
12171229
return false;
12181230

1231+
// Otherwise, if the class is attached to a module, the tables are uniquely
1232+
// emitted in the object for the module unit in which it is defined.
1233+
if (RD->isInNamedModule())
1234+
return RD->shouldEmitInExternalSource();
1235+
12191236
// Otherwise, if the class doesn't have a key function (possibly
12201237
// anymore), the vtable must be defined here.
12211238
const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD);
12221239
if (!keyFunction)
12231240
return false;
12241241

1225-
const FunctionDecl *Def;
12261242
// Otherwise, if we don't have a definition of the key function, the
12271243
// vtable must be defined somewhere else.
1228-
if (!keyFunction->hasBody(Def))
1229-
return true;
1230-
1231-
assert(Def && "The body of the key function is not assigned to Def?");
1232-
// If the non-inline key function comes from another module unit, the vtable
1233-
// must be defined there.
1234-
return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
1244+
return !keyFunction->hasBody();
12351245
}
12361246

12371247
/// 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
@@ -2308,6 +2308,9 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const {
23082308
if (!canSpeculativelyEmitVTableAsBaseClass(RD))
23092309
return false;
23102310

2311+
if (RD->shouldEmitInExternalSource())
2312+
return false;
2313+
23112314
// For a complete-object vtable (or more specifically, for the VTT), we need
23122315
// to be able to speculatively emit the vtables of all dynamic virtual bases.
23132316
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
@@ -18096,6 +18096,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
1809618096
if (NumInitMethods > 1 || !Def->hasInitMethod())
1809718097
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
1809818098
}
18099+
18100+
// If we're defining a dynamic class in a module interface unit, we always
18101+
// need to produce the vtable for it, even if the vtable is not used in the
18102+
// current TU.
18103+
//
18104+
// The case where the current class is not dynamic is handled in
18105+
// MarkVTableUsed.
18106+
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
18107+
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
1809918108
}
1810018109

1810118110
// 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
@@ -18484,11 +18484,15 @@ bool Sema::DefineUsedVTables() {
1848418484

1848518485
bool DefineVTable = true;
1848618486

18487-
// If this class has a key function, but that key function is
18488-
// defined in another translation unit, we don't need to emit the
18489-
// vtable even though we're using it.
1849018487
const CXXMethodDecl *KeyFunction = Context.getCurrentKeyFunction(Class);
18491-
if (KeyFunction && !KeyFunction->hasBody()) {
18488+
// V-tables for non-template classes with an owning module are always
18489+
// uniquely emitted in that module.
18490+
if (Class->isInCurrentModuleUnit()) {
18491+
DefineVTable = true;
18492+
} else if (KeyFunction && !KeyFunction->hasBody()) {
18493+
// If this class has a key function, but that key function is
18494+
// defined in another translation unit, we don't need to emit the
18495+
// vtable even though we're using it.
1849218496
// The key function is in another translation unit.
1849318497
DefineVTable = false;
1849418498
TemplateSpecializationKind TSK =
@@ -18533,7 +18537,7 @@ bool Sema::DefineUsedVTables() {
1853318537
DefinedAnything = true;
1853418538
MarkVirtualMembersReferenced(Loc, Class);
1853518539
CXXRecordDecl *Canonical = Class->getCanonicalDecl();
18536-
if (VTablesUsed[Canonical])
18540+
if (VTablesUsed[Canonical] && !Class->shouldEmitInExternalSource())
1853718541
Consumer.HandleVTable(Class);
1853818542

1853918543
// 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
@@ -3927,6 +3927,13 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
39273927
}
39283928
break;
39293929

3930+
case VTABLES_TO_EMIT:
3931+
if (F.Kind == MK_MainFile ||
3932+
getContext().getLangOpts().BuildingPCHWithObjectFile)
3933+
for (unsigned I = 0, N = Record.size(); I != N;)
3934+
VTablesToEmit.push_back(ReadDeclID(F, Record, I));
3935+
break;
3936+
39303937
case IMPORTED_MODULES:
39313938
if (!F.isModule()) {
39323939
// If we aren't loading a module (which has its own exports), make
@@ -8122,6 +8129,10 @@ void ASTReader::PassInterestingDeclToConsumer(Decl *D) {
81228129
Consumer->HandleInterestingDecl(DeclGroupRef(D));
81238130
}
81248131

8132+
void ASTReader::PassVTableToConsumer(CXXRecordDecl *RD) {
8133+
Consumer->HandleVTable(RD);
8134+
}
8135+
81258136
void ASTReader::StartTranslationUnit(ASTConsumer *Consumer) {
81268137
this->Consumer = Consumer;
81278138

clang/lib/Serialization/ASTReaderDecl.cpp

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

42104210
// If we add any new potential interesting decl in the last call, consume it.
42114211
ConsumingPotentialInterestingDecls();
4212+
4213+
for (GlobalDeclID ID : VTablesToEmit) {
4214+
auto *RD = cast<CXXRecordDecl>(GetDecl(ID));
4215+
assert(!RD->shouldEmitInExternalSource());
4216+
PassVTableToConsumer(RD);
4217+
}
4218+
VTablesToEmit.clear();
42124219
}
42134220

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

clang/lib/Serialization/ASTWriter.cpp

Lines changed: 29 additions & 4 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);
@@ -3961,6 +3962,10 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP,
39613962
Stream.EmitRecord(INTERESTING_IDENTIFIERS, InterestingIdents);
39623963
}
39633964

3965+
void ASTWriter::handleVTable(CXXRecordDecl *RD) {
3966+
PendingEmittingVTables.push_back(RD);
3967+
}
3968+
39643969
//===----------------------------------------------------------------------===//
39653970
// DeclContext's Name Lookup Table Serialization
39663971
//===----------------------------------------------------------------------===//
@@ -5163,6 +5168,13 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
51635168
// Write all of the DeclsToCheckForDeferredDiags.
51645169
for (auto *D : SemaRef.DeclsToCheckForDeferredDiags)
51655170
GetDeclRef(D);
5171+
5172+
// Write all classes that need to emit the vtable definitions if required.
5173+
if (isWritingStdCXXNamedModules())
5174+
for (CXXRecordDecl *RD : PendingEmittingVTables)
5175+
GetDeclRef(RD);
5176+
else
5177+
PendingEmittingVTables.clear();
51665178
}
51675179

51685180
void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
@@ -5317,6 +5329,17 @@ void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
53175329
}
53185330
if (!DeleteExprsToAnalyze.empty())
53195331
Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);
5332+
5333+
RecordData VTablesToEmit;
5334+
for (CXXRecordDecl *RD : PendingEmittingVTables) {
5335+
if (!wasDeclEmitted(RD))
5336+
continue;
5337+
5338+
AddDeclRef(RD, VTablesToEmit);
5339+
}
5340+
5341+
if (!VTablesToEmit.empty())
5342+
Stream.EmitRecord(VTABLES_TO_EMIT, VTablesToEmit);
53205343
}
53215344

53225345
ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot,
@@ -6559,10 +6582,12 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) {
65596582
// computed.
65606583
Record->push_back(D->getODRHash());
65616584

6562-
bool ModulesDebugInfo =
6563-
Writer->Context->getLangOpts().ModulesDebugInfo && !D->isDependentType();
6564-
Record->push_back(ModulesDebugInfo);
6565-
if (ModulesDebugInfo)
6585+
bool ModulesCodegen =
6586+
!D->isDependentType() &&
6587+
(Writer->Context->getLangOpts().ModulesDebugInfo ||
6588+
D->isInNamedModule());
6589+
Record->push_back(ModulesCodegen);
6590+
if (ModulesCodegen)
65666591
Writer->AddDeclRef(D, Writer->ModularCodegenDecls);
65676592

65686593
// IsLambda bit is already saved.

clang/lib/Serialization/ASTWriterDecl.cpp

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

1532+
if (D->isCompleteDefinition() && D->isInNamedModule())
1533+
Writer.AddDeclRef(D, Writer.ModularCodegenDecls);
1534+
15321535
// Store (what we currently believe to be) the key function to avoid
15331536
// deserializing every method so we can compute it.
1537+
//
1538+
// FIXME: Avoid adding the key function if the class is defined in
1539+
// module purview since in that case the key function is meaningless.
15341540
if (D->isCompleteDefinition())
15351541
Record.AddDeclRef(Context.getCurrentKeyFunction(D));
15361542

0 commit comments

Comments
 (0)