[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@186828 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp
index 22d5488..0e01d8b 100644
--- a/lib/CodeGen/MicrosoftCXXABI.cpp
+++ b/lib/CodeGen/MicrosoftCXXABI.cpp
@@ -65,11 +65,51 @@
llvm::BasicBlock *EmitCtorCompleteObjectHandler(CodeGenFunction &CGF,
const CXXRecordDecl *RD);
- void BuildDestructorSignature(const CXXDestructorDecl *Ctor,
+ // Background on MSVC destructors
+ // ==============================
+ //
+ // Both Itanium and MSVC ABIs have destructor variants. The variant names
+ // roughly correspond in the following way:
+ // Itanium Microsoft
+ // Base -> no name, just ~Class
+ // Complete -> vbase destructor
+ // Deleting -> scalar deleting destructor
+ // vector deleting destructor
+ //
+ // The base and complete destructors are the same as in Itanium, although the
+ // complete destructor does not accept a VTT parameter when there are virtual
+ // bases. A separate mechanism involving vtordisps is used to ensure that
+ // virtual methods of destroyed subobjects are not called.
+ //
+ // The deleting destructors accept an i32 bitfield as a second parameter. Bit
+ // 1 indicates if the memory should be deleted. Bit 2 indicates if the this
+ // pointer points to an array. The scalar deleting destructor assumes that
+ // bit 2 is zero, and therefore does not contain a loop.
+ //
+ // For virtual destructors, only one entry is reserved in the vftable, and it
+ // always points to the vector deleting destructor. The vector deleting
+ // destructor is the most general, so it can be used to destroy objects in
+ // place, delete single heap objects, or delete arrays.
+ //
+ // A TU defining a non-inline destructor is only guaranteed to emit a base
+ // destructor, and all of the other variants are emitted on an as-needed basis
+ // in COMDATs. Because a non-base destructor can be emitted in a TU that
+ // lacks a definition for the destructor, non-base destructors must always
+ // delegate to or alias the base destructor.
+
+ void BuildDestructorSignature(const CXXDestructorDecl *Dtor,
CXXDtorType Type,
CanQualType &ResTy,
SmallVectorImpl<CanQualType> &ArgTys);
+ /// Non-base dtors should be emitted as delegating thunks in this ABI.
+ bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
+ CXXDtorType DT) const {
+ return DT != Dtor_Base;
+ }
+
+ void EmitCXXDestructors(const CXXDestructorDecl *D);
+
void BuildInstanceFunctionParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params);
@@ -387,6 +427,12 @@
}
}
+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.
+ CGM.EmitGlobal(GlobalDecl(D, Dtor_Base));
+}
+
static bool IsDeletingDtor(GlobalDecl GD) {
const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
if (isa<CXXDestructorDecl>(MD)) {