[MS] Always use base dtors in place of complete/vbase dtors when possible
Summary:
Previously we tried too hard to uphold the fiction that destructor
variants work like they do on Itanium throughout the ABI-neutral parts
of clang. This lead to MS C++ ABI incompatiblities and other bugs. Now,
-mconstructor-aliases will no longer control this ABI detail, and clang
-cc1's LLVM IR output will be this much closer to the clang driver's.
Based on a patch by Zahira Ammarguellat:
https://reviews.llvm.org/D39063
I've tried to move the logic that Zahira added into MicrosoftCXXABI.cpp.
There is only one ABI-specific detail sticking out, and that is in
CodeGenModule::getAddrOfCXXStructor, where we collapse complete dtors to
base dtors in the MS ABI.
This fixes PR32990.
Reviewers: erichkeane, zahiraam, majnemer, rjmccall
Subscribers: cfe-commits
Differential Revision: https://reviews.llvm.org/D44505
llvm-svn: 327732
diff --git a/clang/lib/CodeGen/CGCXX.cpp b/clang/lib/CodeGen/CGCXX.cpp
index 738c635..dfae7d5 100644
--- a/clang/lib/CodeGen/CGCXX.cpp
+++ b/clang/lib/CodeGen/CGCXX.cpp
@@ -242,6 +242,11 @@
if (auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
GD = GlobalDecl(CD, toCXXCtorType(Type));
} else {
+ // Always alias equivalent complete destructors to base destructors in the
+ // MS ABI.
+ if (getTarget().getCXXABI().isMicrosoft() &&
+ Type == StructorType::Complete && MD->getParent()->getNumVBases() == 0)
+ Type = StructorType::Base;
GD = GlobalDecl(cast<CXXDestructorDecl>(MD), toCXXDtorType(Type));
}
diff --git a/clang/lib/CodeGen/CGCXXABI.cpp b/clang/lib/CodeGen/CGCXXABI.cpp
index a27c3e9..0611749 100644
--- a/clang/lib/CodeGen/CGCXXABI.cpp
+++ b/clang/lib/CodeGen/CGCXXABI.cpp
@@ -287,6 +287,20 @@
return nullptr;
}
+void CGCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
+ const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const {
+ // Assume the base C++ ABI has no special rules for destructor variants.
+ CGM.setDLLImportDLLExport(GV, Dtor);
+}
+
+llvm::GlobalValue::LinkageTypes CGCXXABI::getCXXDestructorLinkage(
+ GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
+ // Delegate back to CGM by default.
+ return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
+ /*isConstantVariable=*/false);
+}
+
bool CGCXXABI::NeedsVTTParameter(GlobalDecl GD) {
return false;
}
diff --git a/clang/lib/CodeGen/CGCXXABI.h b/clang/lib/CodeGen/CGCXXABI.h
index 65a4c3d..1e4e132 100644
--- a/clang/lib/CodeGen/CGCXXABI.h
+++ b/clang/lib/CodeGen/CGCXXABI.h
@@ -319,6 +319,14 @@
virtual bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
CXXDtorType DT) const = 0;
+ virtual void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
+ const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const;
+
+ virtual llvm::GlobalValue::LinkageTypes
+ getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const;
+
/// Emit destructor variants required by this ABI.
virtual void EmitCXXDestructors(const CXXDestructorDecl *D) = 0;
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3c503eb..cb1bfc1 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -786,12 +786,10 @@
void CodeGenModule::setDLLImportDLLExport(llvm::GlobalValue *GV,
GlobalDecl GD) const {
const auto *D = dyn_cast<NamedDecl>(GD.getDecl());
+ // C++ destructors have a few C++ ABI specific special cases.
if (const auto *Dtor = dyn_cast_or_null<CXXDestructorDecl>(D)) {
- if (getCXXABI().useThunkForDtorVariant(Dtor, GD.getDtorType())) {
- // Don't dllexport/import destructor thunks.
- GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
- return;
- }
+ getCXXABI().setCXXDestructorDLLStorage(GV, Dtor, GD.getDtorType());
+ return;
}
setDLLImportDLLExport(GV, D);
}
@@ -1074,14 +1072,8 @@
GVALinkage Linkage = getContext().GetGVALinkageForFunction(D);
- if (isa<CXXDestructorDecl>(D) &&
- getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D),
- GD.getDtorType())) {
- // Destructor variants in the Microsoft C++ ABI are always internal or
- // linkonce_odr thunks emitted on an as-needed basis.
- return Linkage == GVA_Internal ? llvm::GlobalValue::InternalLinkage
- : llvm::GlobalValue::LinkOnceODRLinkage;
- }
+ if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(D))
+ return getCXXABI().getCXXDestructorLinkage(Linkage, Dtor, GD.getDtorType());
if (isa<CXXConstructorDecl>(D) &&
cast<CXXConstructorDecl>(D)->isInheritingConstructor() &&
diff --git a/clang/lib/CodeGen/MicrosoftCXXABI.cpp b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
index d48dfbf..221ee76 100644
--- a/clang/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/clang/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -216,6 +216,14 @@
return DT != Dtor_Base;
}
+ void setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
+ const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const override;
+
+ llvm::GlobalValue::LinkageTypes
+ getCXXDestructorLinkage(GVALinkage Linkage, const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const override;
+
void EmitCXXDestructors(const CXXDestructorDecl *D) override;
const CXXRecordDecl *
@@ -1310,6 +1318,52 @@
return Added;
}
+void MicrosoftCXXABI::setCXXDestructorDLLStorage(llvm::GlobalValue *GV,
+ const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const {
+ // Deleting destructor variants are never imported or exported. Give them the
+ // default storage class.
+ if (DT == Dtor_Deleting) {
+ GV->setDLLStorageClass(llvm::GlobalValue::DefaultStorageClass);
+ } else {
+ const NamedDecl *ND = Dtor;
+ CGM.setDLLImportDLLExport(GV, ND);
+ }
+}
+
+llvm::GlobalValue::LinkageTypes MicrosoftCXXABI::getCXXDestructorLinkage(
+ GVALinkage Linkage, const CXXDestructorDecl *Dtor, CXXDtorType DT) const {
+ // Internal things are always internal, regardless of attributes. After this,
+ // we know the thunk is externally visible.
+ if (Linkage == GVA_Internal)
+ return llvm::GlobalValue::InternalLinkage;
+
+ switch (DT) {
+ case Dtor_Base:
+ // The base destructor most closely tracks the user-declared constructor, so
+ // we delegate back to the normal declarator case.
+ return CGM.getLLVMLinkageForDeclarator(Dtor, Linkage,
+ /*isConstantVariable=*/false);
+ case Dtor_Complete:
+ // The complete destructor is like an inline function, but it may be
+ // imported and therefore must be exported as well. This requires changing
+ // the linkage if a DLL attribute is present.
+ if (Dtor->hasAttr<DLLExportAttr>())
+ return llvm::GlobalValue::WeakODRLinkage;
+ if (Dtor->hasAttr<DLLImportAttr>())
+ return llvm::GlobalValue::AvailableExternallyLinkage;
+ return llvm::GlobalValue::LinkOnceODRLinkage;
+ case Dtor_Deleting:
+ // Deleting destructors are like inline functions. They have vague linkage
+ // and are emitted everywhere they are used. They are internal if the class
+ // is internal.
+ return llvm::GlobalValue::LinkOnceODRLinkage;
+ case Dtor_Comdat:
+ llvm_unreachable("MS C++ ABI does not support comdat dtors");
+ }
+ llvm_unreachable("invalid dtor type");
+}
+
void MicrosoftCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) {
// The TU defining a dtor is only guaranteed to emit a base destructor. All
// other destructor variants are delegating thunks.
@@ -1549,6 +1603,12 @@
const CXXDestructorDecl *DD,
CXXDtorType Type, bool ForVirtualBase,
bool Delegating, Address This) {
+ // Use the base destructor variant in place of the complete destructor variant
+ // if the class has no virtual bases. This effectively implements some of the
+ // -mconstructor-aliases optimization, but as part of the MS C++ ABI.
+ if (Type == Dtor_Complete && DD->getParent()->getNumVBases() == 0)
+ Type = Dtor_Base;
+
CGCallee Callee = CGCallee::forDirect(
CGM.getAddrOfCXXStructor(DD, getFromDtorType(Type)),
DD);
@@ -3821,19 +3881,12 @@
static void emitCXXDestructor(CodeGenModule &CGM, const CXXDestructorDecl *dtor,
StructorType dtorType) {
- // The complete destructor is equivalent to the base destructor for
- // classes with no virtual bases, so try to emit it as an alias.
- if (!dtor->getParent()->getNumVBases() &&
- (dtorType == StructorType::Complete || dtorType == StructorType::Base)) {
- bool ProducedAlias = !CGM.TryEmitDefinitionAsAlias(
- GlobalDecl(dtor, Dtor_Complete), GlobalDecl(dtor, Dtor_Base));
- if (ProducedAlias) {
- if (dtorType == StructorType::Complete)
- return;
- if (dtor->isVirtual())
- CGM.getVTables().EmitThunks(GlobalDecl(dtor, Dtor_Complete));
- }
- }
+ // Emit the base destructor if the base and complete (vbase) destructors are
+ // equivalent. This effectively implements -mconstructor-aliases as part of
+ // the ABI.
+ if (dtorType == StructorType::Complete &&
+ dtor->getParent()->getNumVBases() == 0)
+ dtorType = StructorType::Base;
// The base destructor is equivalent to the base destructor of its
// base class if there is exactly one non-virtual base class with a