r186828 - [ms-cxxabi] Emit linkonce complete dtors in TUs that need them

Reid Kleckner reid at kleckner.net
Mon Jul 22 06:51:44 PDT 2013


Author: rnk
Date: Mon Jul 22 08:51:44 2013
New Revision: 186828

URL: http://llvm.org/viewvc/llvm-project?rev=186828&view=rev
Log:
[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

Modified:
    cfe/trunk/include/clang/Basic/TargetCXXABI.h
    cfe/trunk/lib/AST/MicrosoftMangle.cpp
    cfe/trunk/lib/CodeGen/CGCXX.cpp
    cfe/trunk/lib/CodeGen/CGCXXABI.h
    cfe/trunk/lib/CodeGen/CGClass.cpp
    cfe/trunk/lib/CodeGen/CGExprCXX.cpp
    cfe/trunk/lib/CodeGen/CodeGenModule.cpp
    cfe/trunk/lib/CodeGen/CodeGenModule.h
    cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
    cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
    cfe/trunk/test/CodeGenCXX/microsoft-abi-structors.cpp

Modified: cfe/trunk/include/clang/Basic/TargetCXXABI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TargetCXXABI.h?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/TargetCXXABI.h (original)
+++ cfe/trunk/include/clang/Basic/TargetCXXABI.h Mon Jul 22 08:51:44 2013
@@ -152,12 +152,6 @@ public:
     return isItaniumFamily();
   }
 
-  /// \brief Does this ABI have different entrypoints for complete-object
-  /// and base-subobject destructors?
-  bool hasDestructorVariants() const {
-    return isItaniumFamily();
-  }
-
   /// \brief Does this ABI allow virtual bases to be primary base classes?
   bool hasPrimaryVBases() const {
     return isItaniumFamily();

Modified: cfe/trunk/lib/AST/MicrosoftMangle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/MicrosoftMangle.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/AST/MicrosoftMangle.cpp (original)
+++ cfe/trunk/lib/AST/MicrosoftMangle.cpp Mon Jul 22 08:51:44 2013
@@ -522,9 +522,9 @@ MicrosoftCXXNameMangler::mangleUnqualifi
         // use the type we were given.
         mangleCXXDtorType(static_cast<CXXDtorType>(StructorType));
       else
-        // Otherwise, use the complete destructor name. This is relevant if a
+        // Otherwise, use the base destructor name. This is relevant if a
         // class with a destructor is declared within a destructor.
-        mangleCXXDtorType(Dtor_Complete);
+        mangleCXXDtorType(Dtor_Base);
       break;
       
     case DeclarationName::CXXConversionFunctionName:
@@ -594,18 +594,19 @@ void MicrosoftCXXNameMangler::manglePost
 }
 
 void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) {
+  // Microsoft uses the names on the case labels for these dtor variants.  Clang
+  // uses the Itanium terminology internally.  Everything in this ABI delegates
+  // towards the base dtor.
   switch (T) {
-  case Dtor_Deleting:
-    Out << "?_G";
-    return;
-  case Dtor_Base:
-    // FIXME: We should be asked to mangle base dtors.
-    // However, fixing this would require larger changes to the CodeGenModule.
-    // Please put llvm_unreachable here when CGM is changed.
-    // For now, just mangle a base dtor the same way as a complete dtor...
-  case Dtor_Complete:
-    Out << "?1";
-    return;
+  // <operator-name> ::= ?1  # destructor
+  case Dtor_Base: Out << "?1"; return;
+  // <operator-name> ::= ?_D # vbase destructor
+  case Dtor_Complete: Out << "?_D"; return;
+  // <operator-name> ::= ?_G # scalar deleting destructor
+  case Dtor_Deleting: Out << "?_G"; return;
+  // <operator-name> ::= ?_E # vector deleting destructor
+  // FIXME: Add a vector deleting dtor type.  It goes in the vtable, so we need
+  // it.
   }
   llvm_unreachable("Unsupported dtor type?");
 }

Modified: cfe/trunk/lib/CodeGen/CGCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXX.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGCXX.cpp Mon Jul 22 08:51:44 2013
@@ -228,22 +228,6 @@ CodeGenModule::GetAddrOfCXXConstructor(c
                                                       /*ForVTable=*/false));
 }
 
-void CodeGenModule::EmitCXXDestructors(const CXXDestructorDecl *D) {
-  // The destructor in a virtual table is always a 'deleting'
-  // destructor, which calls the complete destructor and then uses the
-  // appropriate operator delete.
-  if (D->isVirtual())
-    EmitGlobal(GlobalDecl(D, Dtor_Deleting));
-
-  // The destructor used for destructing this as a most-derived class;
-  // call the base destructor and then destructs any virtual bases.
-  EmitGlobal(GlobalDecl(D, Dtor_Complete));
-
-  // The destructor used for destructing this as a base class; ignores
-  // virtual bases.
-  EmitGlobal(GlobalDecl(D, Dtor_Base));
-}
-
 void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *dtor,
                                       CXXDtorType dtorType) {
   // The complete destructor is equivalent to the base destructor for
@@ -277,16 +261,27 @@ void CodeGenModule::EmitCXXDestructor(co
 llvm::GlobalValue *
 CodeGenModule::GetAddrOfCXXDestructor(const CXXDestructorDecl *dtor,
                                       CXXDtorType dtorType,
-                                      const CGFunctionInfo *fnInfo) {
+                                      const CGFunctionInfo *fnInfo,
+                                      llvm::FunctionType *fnType) {
+  // If the class has no virtual bases, then the complete and base destructors
+  // are equivalent, for all C++ ABIs supported by clang.  We can save on code
+  // size by calling the base dtor directly, especially if we'd have to emit a
+  // thunk otherwise.
+  // FIXME: We should do this for Itanium, after verifying that nothing breaks.
+  if (dtorType == Dtor_Complete && dtor->getParent()->getNumVBases() == 0 &&
+      getCXXABI().useThunkForDtorVariant(dtor, Dtor_Complete))
+    dtorType = Dtor_Base;
+
   GlobalDecl GD(dtor, dtorType);
 
   StringRef name = getMangledName(GD);
   if (llvm::GlobalValue *existing = GetGlobalValue(name))
     return existing;
 
-  if (!fnInfo) fnInfo = &getTypes().arrangeCXXDestructor(dtor, dtorType);
-
-  llvm::FunctionType *fnType = getTypes().GetFunctionType(*fnInfo);
+  if (!fnType) {
+    if (!fnInfo) fnInfo = &getTypes().arrangeCXXDestructor(dtor, dtorType);
+    fnType = getTypes().GetFunctionType(*fnInfo);
+  }
   return cast<llvm::Function>(GetOrCreateLLVMFunction(name, fnType, GD,
                                                       /*ForVTable=*/false));
 }

Modified: cfe/trunk/lib/CodeGen/CGCXXABI.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCXXABI.h?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGCXXABI.h (original)
+++ cfe/trunk/lib/CodeGen/CGCXXABI.h Mon Jul 22 08:51:44 2013
@@ -244,6 +244,15 @@ public:
                                         CanQualType &ResTy,
                                SmallVectorImpl<CanQualType> &ArgTys) = 0;
 
+  /// Returns true if the given destructor type should be emitted as a linkonce
+  /// delegating thunk, regardless of whether the dtor is defined in this TU or
+  /// not.
+  virtual bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
+                                      CXXDtorType DT) const = 0;
+
+  /// Emit destructor variants required by this ABI.
+  virtual void EmitCXXDestructors(const CXXDestructorDecl *D) = 0;
+
   /// Build the ABI-specific portion of the parameter list for a
   /// function.  This generally involves a 'this' parameter and
   /// possibly some extra data for constructors and destructors.

Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGClass.cpp Mon Jul 22 08:51:44 2013
@@ -1265,16 +1265,19 @@ void CodeGenFunction::EmitDestructorBody
   // If this is the complete variant, just invoke the base variant;
   // the epilogue will destruct the virtual bases.  But we can't do
   // this optimization if the body is a function-try-block, because
-  // we'd introduce *two* handler blocks.
+  // we'd introduce *two* handler blocks.  In the Microsoft ABI, we 
+  // always delegate because we might not have a definition in this TU.
   switch (DtorType) {
   case Dtor_Deleting: llvm_unreachable("already handled deleting case");
 
   case Dtor_Complete:
+    assert((Body || getTarget().getCXXABI().isMicrosoft()) &&
+           "can't emit a dtor without a body for non-Microsoft ABIs");
+
     // Enter the cleanup scopes for virtual bases.
     EnterDtorCleanups(Dtor, Dtor_Complete);
 
-    if (!isTryBody &&
-        CGM.getTarget().getCXXABI().hasDestructorVariants()) {
+    if (!isTryBody) {
       EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false,
                             /*Delegating=*/false, LoadCXXThis());
       break;
@@ -1282,6 +1285,8 @@ void CodeGenFunction::EmitDestructorBody
     // Fallthrough: act like we're in the base variant.
       
   case Dtor_Base:
+    assert(Body);
+
     // Enter the cleanup scopes for fields and non-virtual bases.
     EnterDtorCleanups(Dtor, Dtor_Base);
 

Modified: cfe/trunk/lib/CodeGen/CGExprCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprCXX.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprCXX.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprCXX.cpp Mon Jul 22 08:51:44 2013
@@ -260,7 +260,7 @@ RValue CodeGenFunction::EmitCXXMemberCal
   else
     FInfo = &CGM.getTypes().arrangeCXXMethodDeclaration(CalleeDecl);
 
-  llvm::Type *Ty = CGM.getTypes().GetFunctionType(*FInfo);
+  llvm::FunctionType *Ty = CGM.getTypes().GetFunctionType(*FInfo);
 
   // C++ [class.virtual]p12:
   //   Explicit qualification with the scope operator (5.1) suppresses the
@@ -284,7 +284,7 @@ RValue CodeGenFunction::EmitCXXMemberCal
           ME->hasQualifier())
         Callee = BuildAppleKextVirtualCall(MD, ME->getQualifier(), Ty);
       else if (!DevirtualizedMethod)
-        Callee = CGM.GetAddrOfFunction(GlobalDecl(Dtor, Dtor_Complete), Ty);
+        Callee = CGM.GetAddrOfCXXDestructor(Dtor, Dtor_Complete, FInfo, Ty);
       else {
         const CXXDestructorDecl *DDtor =
           cast<CXXDestructorDecl>(DevirtualizedMethod);

Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Mon Jul 22 08:51:44 2013
@@ -513,6 +513,12 @@ void CodeGenModule::EmitCtorList(const C
 llvm::GlobalValue::LinkageTypes
 CodeGenModule::getFunctionLinkage(GlobalDecl GD) {
   const FunctionDecl *D = cast<FunctionDecl>(GD.getDecl());
+
+  if (isa<CXXDestructorDecl>(D) &&
+      getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D),
+                                         GD.getDtorType()))
+    return llvm::Function::LinkOnceODRLinkage;
+
   GVALinkage Linkage = getContext().GetGVALinkageForFunction(D);
 
   if (Linkage == GVA_Internal)
@@ -1348,6 +1354,14 @@ CodeGenModule::GetOrCreateLLVMFunction(S
     return llvm::ConstantExpr::getBitCast(Entry, Ty->getPointerTo());
   }
 
+  // All MSVC dtors other than the base dtor are linkonce_odr and delegate to
+  // each other bottoming out with the base dtor.  Therefore we emit non-base
+  // dtors on usage, even if there is no dtor definition in the TU.
+  if (D && isa<CXXDestructorDecl>(D) &&
+      getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(D),
+                                         GD.getDtorType()))
+    DeferredDeclsToEmit.push_back(GD);
+
   // This function doesn't have a complete type (for example, the return
   // type is an incomplete struct). Use a fake type instead, and make
   // sure not to try to set attributes.
@@ -2869,7 +2883,7 @@ void CodeGenModule::EmitTopLevelDecl(Dec
   case Decl::CXXDestructor:
     if (cast<FunctionDecl>(D)->isLateTemplateParsed())
       return;
-    EmitCXXDestructors(cast<CXXDestructorDecl>(D));
+    getCXXABI().EmitCXXDestructors(cast<CXXDestructorDecl>(D));
     break;
 
   case Decl::StaticAssert:

Modified: cfe/trunk/lib/CodeGen/CodeGenModule.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.h?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CodeGenModule.h (original)
+++ cfe/trunk/lib/CodeGen/CodeGenModule.h Mon Jul 22 08:51:44 2013
@@ -752,7 +752,8 @@ public:
   /// given type.
   llvm::GlobalValue *GetAddrOfCXXDestructor(const CXXDestructorDecl *dtor,
                                             CXXDtorType dtorType,
-                                            const CGFunctionInfo *fnInfo = 0);
+                                            const CGFunctionInfo *fnInfo = 0,
+                                            llvm::FunctionType *fnType = 0);
 
   /// getBuiltinLibFunction - Given a builtin id for a function like
   /// "__builtin_fabsf", return a Function* for "fabsf".
@@ -980,6 +981,10 @@ public:
     DeferredVTables.push_back(RD);
   }
 
+  /// EmitGlobal - Emit code for a singal global function or var decl. Forward
+  /// declarations are emitted lazily.
+  void EmitGlobal(GlobalDecl D);
+
 private:
   llvm::GlobalValue *GetGlobalValue(StringRef Ref);
 
@@ -1011,10 +1016,6 @@ private:
                              llvm::Function *F,
                              bool IsIncompleteFunction);
 
-  /// EmitGlobal - Emit code for a singal global function or var decl. Forward
-  /// declarations are emitted lazily.
-  void EmitGlobal(GlobalDecl D);
-
   void EmitGlobalDefinition(GlobalDecl D);
 
   void EmitGlobalFunctionDefinition(GlobalDecl GD);
@@ -1040,10 +1041,6 @@ private:
   /// a C++ constructor Decl.
   void EmitCXXConstructor(const CXXConstructorDecl *D, CXXCtorType Type);
 
-  /// EmitCXXDestructors - Emit destructors (base, complete) from a
-  /// C++ destructor Decl.
-  void EmitCXXDestructors(const CXXDestructorDecl *D);
-
   /// EmitCXXDestructor - Emit a single destructor with the given type from
   /// a C++ destructor Decl.
   void EmitCXXDestructor(const CXXDestructorDecl *D, CXXDtorType Type);

Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Mon Jul 22 08:51:44 2013
@@ -113,6 +113,16 @@ public:
                                 CanQualType &ResTy,
                                 SmallVectorImpl<CanQualType> &ArgTys);
 
+  bool useThunkForDtorVariant(const CXXDestructorDecl *Dtor,
+                              CXXDtorType DT) const {
+    // Itanium does not emit any destructor variant as an inline thunk.
+    // Delegating may occur as an optimization, but all variants are either
+    // emitted with external linkage or as linkonce if they are inline and used.
+    return false;
+  }
+
+  void EmitCXXDestructors(const CXXDestructorDecl *D);
+
   void BuildInstanceFunctionParams(CodeGenFunction &CGF,
                                    QualType &ResTy,
                                    FunctionArgList &Params);
@@ -761,6 +771,22 @@ void ItaniumCXXABI::BuildDestructorSigna
     ArgTys.push_back(Context.getPointerType(Context.VoidPtrTy));
 }
 
+void ItaniumCXXABI::EmitCXXDestructors(const CXXDestructorDecl *D) {
+  // The destructor in a virtual table is always a 'deleting'
+  // destructor, which calls the complete destructor and then uses the
+  // appropriate operator delete.
+  if (D->isVirtual())
+    CGM.EmitGlobal(GlobalDecl(D, Dtor_Deleting));
+
+  // The destructor used for destructing this as a most-derived class;
+  // call the base destructor and then destructs any virtual bases.
+  CGM.EmitGlobal(GlobalDecl(D, Dtor_Complete));
+
+  // The destructor used for destructing this as a base class; ignores
+  // virtual bases.
+  CGM.EmitGlobal(GlobalDecl(D, Dtor_Base));
+}
+
 void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF,
                                                 QualType &ResTy,
                                                 FunctionArgList &Params) {

Modified: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp (original)
+++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp Mon Jul 22 08:51:44 2013
@@ -65,11 +65,51 @@ public:
   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::BuildDestructorSig
   }
 }
 
+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)) {

Modified: cfe/trunk/test/CodeGenCXX/microsoft-abi-structors.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/microsoft-abi-structors.cpp?rev=186828&r1=186827&r2=186828&view=diff
==============================================================================
--- cfe/trunk/test/CodeGenCXX/microsoft-abi-structors.cpp (original)
+++ cfe/trunk/test/CodeGenCXX/microsoft-abi-structors.cpp Mon Jul 22 08:51:44 2013
@@ -1,15 +1,15 @@
-// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 -fno-rtti > %t 2>&1
+// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 -fno-rtti > %t
 // RUN: FileCheck %s < %t
-// Using a different check prefix as the inline destructors might be placed
-// anywhere in the output.
-// RUN: FileCheck --check-prefix=DTORS %s < %t
+// vftables are emitted very late, so do another pass to try to keep the checks
+// in source order.
+// RUN: FileCheck --check-prefix DTORS %s < %t
 
 namespace basic {
 
 class A {
  public:
   A() { }
-  ~A() { }
+  ~A();
 };
 
 void no_constructor_destructor_infinite_recursion() {
@@ -21,7 +21,9 @@ void no_constructor_destructor_infinite_
 // CHECK-NEXT:   [[T1:%[.0-9A-Z_a-z]+]] = load %"class.basic::A"** [[THIS_ADDR]]
 // CHECK-NEXT:   ret %"class.basic::A"* [[T1]]
 // CHECK-NEXT: }
+}
 
+A::~A() {
 // Make sure that the destructor doesn't call itself:
 // CHECK: define {{.*}} @"\01??1A at basic@@QAE at XZ"
 // CHECK-NOT: call void @"\01??1A at basic@@QAE at XZ"
@@ -40,12 +42,6 @@ B::B() {
 
 struct C {
   virtual ~C() {
-// Complete destructor first:
-// DTORS: define {{.*}} x86_thiscallcc void @"\01??1C at basic@@UAE at XZ"(%"struct.basic::C"* %this)
-
-// Then, the scalar deleting destructor (used in the vtable):
-// FIXME: add a test that verifies that the out-of-line scalar deleting
-// destructor is linkonce_odr too.
 // DTORS:      define linkonce_odr x86_thiscallcc void @"\01??_GC at basic@@UAEPAXI at Z"(%"struct.basic::C"* %this, i1 zeroext %should_call_delete)
 // DTORS:        %[[FROMBOOL:[0-9a-z]+]] = zext i1 %should_call_delete to i8
 // DTORS-NEXT:   store i8 %[[FROMBOOL]], i8* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 1
@@ -56,7 +52,7 @@ struct C {
 //
 // DTORS:      [[CALL_DELETE_LABEL]]
 // DTORS-NEXT:   %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8*
-// DTORS-NEXT:   call void @"\01??3 at YAXPAX@Z"(i8* %[[THIS_AS_VOID]]) [[NUW:#[0-9]+]]
+// DTORS-NEXT:   call void @"\01??3 at YAXPAX@Z"(i8* %[[THIS_AS_VOID]])
 // DTORS-NEXT:   br label %[[CONTINUE_LABEL]]
 //
 // DTORS:      [[CONTINUE_LABEL]]
@@ -119,8 +115,6 @@ struct D {
 
 void use_D() { D c; }
 
-// DTORS: attributes [[NUW]] = {{[{].*}} nounwind {{.*[}]}}
-
 } // end namespace basic
 
 
@@ -228,3 +222,60 @@ E::E() {
 }
 
 } // end namespace constructors
+
+namespace dtors {
+
+struct A {
+  ~A();
+};
+
+void call_nv_complete(A *a) {
+  a->~A();
+// CHECK: define void @"\01?call_nv_complete at dtors@@YAXPAUA at 1@@Z"
+// CHECK: call x86_thiscallcc void @"\01??1A at dtors@@QAE at XZ"
+// CHECK: ret
+}
+
+// CHECK: declare x86_thiscallcc void @"\01??1A at dtors@@QAE at XZ"
+
+// Now try some virtual bases, where we need the complete dtor.
+
+struct B : virtual A { ~B(); };
+struct C : virtual A { ~C(); };
+struct D : B, C { ~D(); };
+
+void call_vbase_complete(D *d) {
+  d->~D();
+// CHECK: define void @"\01?call_vbase_complete at dtors@@YAXPAUD at 1@@Z"
+// CHECK: call x86_thiscallcc void @"\01??_DD at dtors@@QAE at XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: ret
+}
+
+// The complete dtor should call the base dtors for D and the vbase A (once).
+// CHECK: define linkonce_odr x86_thiscallcc void @"\01??_DD at dtors@@QAE at XZ"
+// CHECK-NOT: call
+// CHECK: call x86_thiscallcc void @"\01??1D at dtors@@QAE at XZ"
+// CHECK-NOT: call
+// CHECK: call x86_thiscallcc void @"\01??1A at dtors@@QAE at XZ"
+// CHECK-NOT: call
+// CHECK: ret
+
+void destroy_d_complete() {
+  D d;
+// CHECK: define void @"\01?destroy_d_complete at dtors@@YAXXZ"
+// CHECK: call x86_thiscallcc void @"\01??_DD at dtors@@QAE at XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: ret
+}
+
+// FIXME: Clang manually inlines the deletion, so we don't get a call to the
+// deleting dtor (_G).  The only way to call deleting dtors currently is through
+// a vftable.
+void call_nv_deleting_dtor(D *d) {
+  delete d;
+// CHECK: define void @"\01?call_nv_deleting_dtor at dtors@@YAXPAUD at 1@@Z"
+// CHECK: call x86_thiscallcc void @"\01??_DD at dtors@@QAE at XZ"(%"struct.dtors::D"* %{{[^,]+}})
+// CHECK: call void @"\01??3 at YAXPAX@Z"
+// CHECK: ret
+}
+
+}





More information about the cfe-commits mailing list