Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reland [C++20] [Modules] [Itanium ABI] Generate the vtable in the mod… #102287

Merged
merged 3 commits into from
Aug 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions clang/include/clang/AST/DeclBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,13 @@ class alignas(8) Decl {
/// Whether this declaration comes from another module unit.
bool isInAnotherModuleUnit() const;

/// Whether this declaration comes from the same module unit being compiled.
bool isInCurrentModuleUnit() const;

/// Whether the definition of the declaration should be emitted in external
/// sources.
bool shouldEmitInExternalSource() const;

/// Whether this declaration comes from explicit global module.
bool isFromExplicitGlobalModule() const;

Expand Down
3 changes: 3 additions & 0 deletions clang/include/clang/Serialization/ASTBitCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,9 @@ enum ASTRecordTypes {

/// Record code for \#pragma clang unsafe_buffer_usage begin/end
PP_UNSAFE_BUFFER_USAGE = 69,

/// Record code for vtables to emit.
VTABLES_TO_EMIT = 70,
};

/// Record types used within a source manager block.
Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Serialization/ASTReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,11 @@ class ASTReader
/// the consumer eagerly.
SmallVector<GlobalDeclID, 16> EagerlyDeserializedDecls;

/// The IDs of all vtables to emit. The referenced declarations are passed
/// to the consumers' HandleVTable eagerly after passing
/// EagerlyDeserializedDecls.
SmallVector<GlobalDeclID, 16> VTablesToEmit;

/// The IDs of all tentative definitions stored in the chain.
///
/// Sema keeps track of all tentative definitions in a TU because it has to
Expand Down Expand Up @@ -1500,6 +1505,7 @@ class ASTReader
bool isConsumerInterestedIn(Decl *D);
void PassInterestingDeclsToConsumer();
void PassInterestingDeclToConsumer(Decl *D);
void PassVTableToConsumer(CXXRecordDecl *RD);

void finishPendingActions();
void diagnoseOdrViolations();
Expand Down
7 changes: 7 additions & 0 deletions clang/include/clang/Serialization/ASTWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,10 @@ class ASTWriter : public ASTDeserializationListener,
std::vector<SourceRange> NonAffectingRanges;
std::vector<SourceLocation::UIntTy> NonAffectingOffsetAdjustments;

/// A list of classes which need to emit the VTable in the corresponding
/// object file.
llvm::SmallVector<CXXRecordDecl *> PendingEmittingVTables;

/// Computes input files that didn't affect compilation of the current module,
/// and initializes data structures necessary for leaving those files out
/// during \c SourceManager serialization.
Expand Down Expand Up @@ -857,6 +861,8 @@ class ASTWriter : public ASTDeserializationListener,
return PredefinedDecls.count(D);
}

void handleVTable(CXXRecordDecl *RD);

private:
// ASTDeserializationListener implementation
void ReaderInitialized(ASTReader *Reader) override;
Expand Down Expand Up @@ -951,6 +957,7 @@ class PCHGenerator : public SemaConsumer {

void InitializeSema(Sema &S) override { SemaPtr = &S; }
void HandleTranslationUnit(ASTContext &Ctx) override;
void HandleVTable(CXXRecordDecl *RD) override { Writer.handleVTable(RD); }
ASTMutationListener *GetASTMutationListener() override;
ASTDeserializationListener *GetASTDeserializationListener() override;
bool hasEmittedPCH() const { return Buffer->IsComplete; }
Expand Down
3 changes: 1 addition & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12431,8 +12431,7 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) {
!isMSStaticDataMemberInlineDefinition(VD))
return false;

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

// Variables that can be needed in other TUs are required.
Expand Down
34 changes: 25 additions & 9 deletions clang/lib/AST/DeclBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1124,20 +1124,36 @@ bool Decl::isInAnotherModuleUnit() const {
if (!M)
return false;

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

// A global module without parent implies that we're parsing the global
// module. So it can't be in another module unit.
if (M->isGlobalModule())
return M != getASTContext().getCurrentNamedModule();
}

bool Decl::isInCurrentModuleUnit() const {
auto *M = getOwningModule();

if (!M || !M->isNamedModule())
return false;

assert(M->isNamedModule() && "New module kind?");
return M != getASTContext().getCurrentNamedModule();
return M == getASTContext().getCurrentNamedModule();
}

bool Decl::shouldEmitInExternalSource() const {
ExternalASTSource *Source = getASTContext().getExternalSource();
if (!Source)
return false;

return Source->hasExternalDefinitions(this) == ExternalASTSource::EK_Always;
}

bool Decl::isFromExplicitGlobalModule() const {
Expand Down
56 changes: 33 additions & 23 deletions clang/lib/CodeGen/CGVTables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1081,29 +1081,41 @@ llvm::GlobalVariable::LinkageTypes
CodeGenModule::getVTableLinkage(const CXXRecordDecl *RD) {
if (!RD->isExternallyVisible())
return llvm::GlobalVariable::InternalLinkage;

// We're at the end of the translation unit, so the current key
// function is fully correct.
const CXXMethodDecl *keyFunction = Context.getCurrentKeyFunction(RD);
if (keyFunction && !RD->hasAttr<DLLImportAttr>()) {

// In windows, the linkage of vtable is not related to modules.
bool IsInNamedModule = !getTarget().getCXXABI().isMicrosoft() &&
RD->isInNamedModule();
// If the CXXRecordDecl is not in a module unit, we need to get
// its key function. We're at the end of the translation unit, so the current
// key function is fully correct.
const CXXMethodDecl *keyFunction =
IsInNamedModule ? nullptr : Context.getCurrentKeyFunction(RD);
if (IsInNamedModule || (keyFunction && !RD->hasAttr<DLLImportAttr>())) {
// If this class has a key function, use that to determine the
// linkage of the vtable.
const FunctionDecl *def = nullptr;
if (keyFunction->hasBody(def))
if (keyFunction && keyFunction->hasBody(def))
keyFunction = cast<CXXMethodDecl>(def);

switch (keyFunction->getTemplateSpecializationKind()) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
bool IsExternalDefinition =
IsInNamedModule ? RD->shouldEmitInExternalSource() : !def;

TemplateSpecializationKind Kind =
IsInNamedModule ? RD->getTemplateSpecializationKind()
: keyFunction->getTemplateSpecializationKind();

switch (Kind) {
case TSK_Undeclared:
case TSK_ExplicitSpecialization:
assert(
(def || CodeGenOpts.OptimizationLevel > 0 ||
(IsInNamedModule || def || CodeGenOpts.OptimizationLevel > 0 ||
CodeGenOpts.getDebugInfo() != llvm::codegenoptions::NoDebugInfo) &&
"Shouldn't query vtable linkage without key function, "
"optimizations, or debug info");
if (!def && CodeGenOpts.OptimizationLevel > 0)
"Shouldn't query vtable linkage without the class in module units, "
"key function, optimizations, or debug info");
if (IsExternalDefinition && CodeGenOpts.OptimizationLevel > 0)
return llvm::GlobalVariable::AvailableExternallyLinkage;

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

case TSK_ExplicitInstantiationDeclaration:
llvm_unreachable("Should not have been asked to emit this");
}
}
}

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

// Otherwise, if the class is attached to a module, the tables are uniquely
// emitted in the object for the module unit in which it is defined.
if (RD->isInNamedModule())
return RD->shouldEmitInExternalSource();

// Otherwise, if the class doesn't have a key function (possibly
// anymore), the vtable must be defined here.
const CXXMethodDecl *keyFunction = CGM.getContext().getCurrentKeyFunction(RD);
if (!keyFunction)
return false;

const FunctionDecl *Def;
// Otherwise, if we don't have a definition of the key function, the
// vtable must be defined somewhere else.
if (!keyFunction->hasBody(Def))
return true;

assert(Def && "The body of the key function is not assigned to Def?");
// If the non-inline key function comes from another module unit, the vtable
// must be defined there.
return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
return !keyFunction->hasBody();
}

/// Given that we're currently at the end of the translation unit, and
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2308,6 +2308,9 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const {
if (!canSpeculativelyEmitVTableAsBaseClass(RD))
return false;

if (RD->shouldEmitInExternalSource())
return false;

// For a complete-object vtable (or more specifically, for the VTT), we need
// to be able to speculatively emit the vtables of all dynamic virtual bases.
for (const auto &B : RD->vbases()) {
Expand Down
9 changes: 9 additions & 0 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18075,6 +18075,15 @@ void Sema::ActOnTagFinishDefinition(Scope *S, Decl *TagD,
if (NumInitMethods > 1 || !Def->hasInitMethod())
Diag(RD->getLocation(), diag::err_sycl_special_type_num_init_method);
}

// If we're defining a dynamic class in a module interface unit, we always
// need to produce the vtable for it, even if the vtable is not used in the
// current TU.
//
// The case where the current class is not dynamic is handled in
// MarkVTableUsed.
if (getCurrentModule() && getCurrentModule()->isInterfaceOrPartition())
MarkVTableUsed(RD->getLocation(), RD, /*DefinitionRequired=*/true);
}

// Exit this scope of this tag's definition.
Expand Down
14 changes: 9 additions & 5 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18484,11 +18484,15 @@ bool Sema::DefineUsedVTables() {

bool DefineVTable = true;

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

// Warn if we're emitting a weak vtable. The vtable will be weak if there is
Expand Down
11 changes: 11 additions & 0 deletions clang/lib/Serialization/ASTReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3927,6 +3927,13 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
}
break;

case VTABLES_TO_EMIT:
if (F.Kind == MK_MainFile ||
getContext().getLangOpts().BuildingPCHWithObjectFile)
for (unsigned I = 0, N = Record.size(); I != N;)
VTablesToEmit.push_back(ReadDeclID(F, Record, I));
break;

case IMPORTED_MODULES:
if (!F.isModule()) {
// If we aren't loading a module (which has its own exports), make
Expand Down Expand Up @@ -8122,6 +8129,10 @@ void ASTReader::PassInterestingDeclToConsumer(Decl *D) {
Consumer->HandleInterestingDecl(DeclGroupRef(D));
}

void ASTReader::PassVTableToConsumer(CXXRecordDecl *RD) {
Consumer->HandleVTable(RD);
}

void ASTReader::StartTranslationUnit(ASTConsumer *Consumer) {
this->Consumer = Consumer;

Expand Down
7 changes: 7 additions & 0 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4209,6 +4209,13 @@ void ASTReader::PassInterestingDeclsToConsumer() {

// If we add any new potential interesting decl in the last call, consume it.
ConsumingPotentialInterestingDecls();

for (GlobalDeclID ID : VTablesToEmit) {
auto *RD = cast<CXXRecordDecl>(GetDecl(ID));
assert(!RD->shouldEmitInExternalSource());
PassVTableToConsumer(RD);
}
VTablesToEmit.clear();
}

void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
Expand Down
33 changes: 29 additions & 4 deletions clang/lib/Serialization/ASTWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(DECLS_TO_CHECK_FOR_DEFERRED_DIAGS);
RECORD(PP_ASSUME_NONNULL_LOC);
RECORD(PP_UNSAFE_BUFFER_USAGE);
RECORD(VTABLES_TO_EMIT);

// SourceManager Block.
BLOCK(SOURCE_MANAGER_BLOCK);
Expand Down Expand Up @@ -3961,6 +3962,10 @@ void ASTWriter::WriteIdentifierTable(Preprocessor &PP,
Stream.EmitRecord(INTERESTING_IDENTIFIERS, InterestingIdents);
}

void ASTWriter::handleVTable(CXXRecordDecl *RD) {
PendingEmittingVTables.push_back(RD);
}

//===----------------------------------------------------------------------===//
// DeclContext's Name Lookup Table Serialization
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -5163,6 +5168,13 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
// Write all of the DeclsToCheckForDeferredDiags.
for (auto *D : SemaRef.DeclsToCheckForDeferredDiags)
GetDeclRef(D);

// Write all classes that need to emit the vtable definitions if required.
if (isWritingStdCXXNamedModules())
for (CXXRecordDecl *RD : PendingEmittingVTables)
GetDeclRef(RD);
else
PendingEmittingVTables.clear();
}

void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
Expand Down Expand Up @@ -5317,6 +5329,17 @@ void ASTWriter::WriteSpecialDeclRecords(Sema &SemaRef) {
}
if (!DeleteExprsToAnalyze.empty())
Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);

RecordData VTablesToEmit;
for (CXXRecordDecl *RD : PendingEmittingVTables) {
if (!wasDeclEmitted(RD))
continue;
mizvekov marked this conversation as resolved.
Show resolved Hide resolved

AddDeclRef(RD, VTablesToEmit);
}

if (!VTablesToEmit.empty())
Stream.EmitRecord(VTABLES_TO_EMIT, VTablesToEmit);
}

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

bool ModulesDebugInfo =
Writer->Context->getLangOpts().ModulesDebugInfo && !D->isDependentType();
Record->push_back(ModulesDebugInfo);
if (ModulesDebugInfo)
bool ModulesCodegen =
!D->isDependentType() &&
(Writer->Context->getLangOpts().ModulesDebugInfo ||
D->isInNamedModule());
Record->push_back(ModulesCodegen);
if (ModulesCodegen)
Writer->AddDeclRef(D, Writer->ModularCodegenDecls);

// IsLambda bit is already saved.
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1529,8 +1529,14 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
if (D->isThisDeclarationADefinition())
Record.AddCXXDefinitionData(D);

if (D->isCompleteDefinition() && D->isInNamedModule())
Writer.AddDeclRef(D, Writer.ModularCodegenDecls);

// Store (what we currently believe to be) the key function to avoid
// deserializing every method so we can compute it.
//
// FIXME: Avoid adding the key function if the class is defined in
// module purview since in that case the key function is meaningless.
if (D->isCompleteDefinition())
Record.AddDeclRef(Context.getCurrentKeyFunction(D));

Expand Down
Loading
Loading