]> granicus.if.org Git - clang/commitdiff
Make deleting and complete dtor variants defer to other dtor variants by
authorJohn McCall <rjmccall@apple.com>
Thu, 18 Feb 2010 19:59:28 +0000 (19:59 +0000)
committerJohn McCall <rjmccall@apple.com>
Thu, 18 Feb 2010 19:59:28 +0000 (19:59 +0000)
calling them as subroutines.  This triggers whenever the alias optimization
doesn't, i.e. when the dtor has linkonce linkage or there are virtual bases
or it's the deleting dtor.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@96605 91177308-0d34-0410-b5e6-96231b3b80d8

lib/CodeGen/CGClass.cpp
lib/CodeGen/CodeGenFunction.cpp
test/CodeGenCXX/virtual-base-destructor-call.cpp
test/CodeGenCXX/virtual-destructor-calls.cpp

index 863b09c86a99c29b71796931e924d93483cbef3e..7b8e736900e11236d70a3e9b6f13f1c1f9f52c21 100644 (file)
@@ -477,12 +477,21 @@ static llvm::Value *GetVTTParameter(CodeGenFunction &CGF, GlobalDecl GD) {
   
   const CXXRecordDecl *RD = cast<CXXMethodDecl>(CGF.CurFuncDecl)->getParent();
   const CXXRecordDecl *Base = cast<CXXMethodDecl>(GD.getDecl())->getParent();
-  
+
   llvm::Value *VTT;
 
-  uint64_t SubVTTIndex = 
-    CGF.CGM.getVtableInfo().getSubVTTIndex(RD, Base);
-  assert(SubVTTIndex != 0 && "Sub-VTT index must be greater than zero!");
+  uint64_t SubVTTIndex;
+
+  // If the record matches the base, this is the complete ctor/dtor
+  // variant calling the base variant in a class with virtual bases.
+  if (RD == Base) {
+    assert(!CGVtableInfo::needsVTTParameter(CGF.CurGD) &&
+           "doing no-op VTT offset in base dtor/ctor?");
+    SubVTTIndex = 0;
+  } else {
+    SubVTTIndex = CGF.CGM.getVtableInfo().getSubVTTIndex(RD, Base);
+    assert(SubVTTIndex != 0 && "Sub-VTT index must be greater than zero!");
+  }
   
   if (CGVtableInfo::needsVTTParameter(CGF.CurGD)) {
     // A VTT parameter was passed to the constructor, use it.
@@ -962,7 +971,6 @@ void CodeGenFunction::EmitCtorPrologue(const CXXConstructorDecl *CD,
 /// EmitDtorEpilogue - Emit all code that comes at the end of class's
 /// destructor. This is to call destructors on members and base classes
 /// in reverse order of their construction.
-/// FIXME: This needs to take a CXXDtorType.
 void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD,
                                        CXXDtorType DtorType) {
   assert(!DD->isTrivial() &&
@@ -970,6 +978,44 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD,
 
   const CXXRecordDecl *ClassDecl = DD->getParent();
 
+  // In a deleting destructor, we've already called the complete
+  // destructor as a subroutine, so we just have to delete the
+  // appropriate value.
+  if (DtorType == Dtor_Deleting) {
+    assert(DD->getOperatorDelete() && 
+           "operator delete missing - EmitDtorEpilogue");
+    EmitDeleteCall(DD->getOperatorDelete(), LoadCXXThis(),
+                   getContext().getTagDeclType(ClassDecl));
+    return;
+  }
+
+  // For complete destructors, we've already called the base
+  // destructor (in GenerateBody), so we just need to destruct all the
+  // virtual bases.
+  if (DtorType == Dtor_Complete) {
+    // Handle virtual bases.
+    for (CXXRecordDecl::reverse_base_class_const_iterator I = 
+           ClassDecl->vbases_rbegin(), E = ClassDecl->vbases_rend();
+              I != E; ++I) {
+      const CXXBaseSpecifier &Base = *I;
+      CXXRecordDecl *BaseClassDecl
+        = cast<CXXRecordDecl>(Base.getType()->getAs<RecordType>()->getDecl());
+    
+      // Ignore trivial destructors.
+      if (BaseClassDecl->hasTrivialDestructor())
+        continue;
+      const CXXDestructorDecl *D = BaseClassDecl->getDestructor(getContext());
+      llvm::Value *V = GetAddressOfBaseOfCompleteClass(LoadCXXThis(),
+                                                       true,
+                                                       ClassDecl,
+                                                       BaseClassDecl);
+      EmitCXXDestructorCall(D, Dtor_Base, V);
+    }
+    return;
+  }
+
+  assert(DtorType == Dtor_Base);
+
   // Collect the fields.
   llvm::SmallVector<const FieldDecl *, 16> FieldDecls;
   for (CXXRecordDecl::field_iterator I = ClassDecl->field_begin(),
@@ -1042,37 +1088,6 @@ void CodeGenFunction::EmitDtorEpilogue(const CXXDestructorDecl *DD,
                                            /*NullCheckValue=*/false);
     EmitCXXDestructorCall(D, Dtor_Base, V);
   }
-
-  // If we're emitting a base destructor, we don't want to emit calls to the
-  // virtual bases.
-  if (DtorType == Dtor_Base)
-    return;
-  
-  // Handle virtual bases.
-  for (CXXRecordDecl::reverse_base_class_const_iterator I = 
-       ClassDecl->vbases_rbegin(), E = ClassDecl->vbases_rend(); I != E; ++I) {
-    const CXXBaseSpecifier &Base = *I;
-    CXXRecordDecl *BaseClassDecl
-      = cast<CXXRecordDecl>(Base.getType()->getAs<RecordType>()->getDecl());
-    
-    // Ignore trivial destructors.
-    if (BaseClassDecl->hasTrivialDestructor())
-      continue;
-    const CXXDestructorDecl *D = BaseClassDecl->getDestructor(getContext());
-    llvm::Value *V = GetAddressOfBaseOfCompleteClass(LoadCXXThis(),
-                                                     true,
-                                                     ClassDecl,
-                                                     BaseClassDecl);
-    EmitCXXDestructorCall(D, Dtor_Base, V);
-  }
-    
-  // If we have a deleting destructor, emit a call to the delete operator.
-  if (DtorType == Dtor_Deleting) {
-    assert(DD->getOperatorDelete() && 
-           "operator delete missing - EmitDtorEpilogue");
-    EmitDeleteCall(DD->getOperatorDelete(), LoadCXXThis(),
-                   getContext().getTagDeclType(ClassDecl));
-  }
 }
 
 /// EmitCXXAggrConstructorCall - This routine essentially creates a (nested)
index ddf4e6431d02b61dd69a2f5c750427753bc7ca8a..98113a700ec85115ff05732a0f498103aca70201 100644 (file)
@@ -248,39 +248,69 @@ void CodeGenFunction::GenerateBody(GlobalDecl GD, llvm::Function *Fn,
   Stmt *Body = FD->getBody();
   assert((Body || FD->isImplicit()) && "non-implicit function def has no body");
 
+  bool SkipBody = false; // should get jump-threaded
+
   // Emit special ctor/dtor prologues.
-  llvm::BasicBlock *DtorEpilogue = 0;
   if (const CXXConstructorDecl *CD = dyn_cast<CXXConstructorDecl>(FD)) {
+    // Emit the constructor prologue, i.e. the base and member initializers.
     EmitCtorPrologue(CD, GD.getCtorType());
+
+    // TODO: for complete, non-varargs variants, we can get away with
+    // just emitting the vbase initializers, then calling the base
+    // constructor.
+
   } else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(FD)) {
-    DtorEpilogue = createBasicBlock("dtor.epilogue");
+    // In all cases, if there's an exception in the body (or delegate)
+    // we'll still need to run the epilogue.
+    llvm::BasicBlock *DtorEpilogue = createBasicBlock("dtor.epilogue");
     PushCleanupBlock(DtorEpilogue);
 
-    InitializeVtablePtrs(DD->getParent());
+    // If this is the deleting variant, invoke the complete variant;
+    // the epilogue will call the appropriate operator delete().
+    if (GD.getDtorType() == Dtor_Deleting) {
+      EmitCXXDestructorCall(DD, Dtor_Complete, LoadCXXThis());
+      SkipBody = true;
+
+    // If this is the complete variant, just invoke the base variant;
+    // the epilogue will destruct the virtual bases.
+    } else if (GD.getDtorType() == Dtor_Complete) {
+      EmitCXXDestructorCall(DD, Dtor_Base, LoadCXXThis());
+      SkipBody = true;
+
+    // Otherwise, we're in the base variant, so we need to ensure the
+    // vtable ptrs are right before emitting the body.
+    } else {
+      InitializeVtablePtrs(DD->getParent());
+    }
   }
 
   // Emit the body of the function.
-  if (!Body)
+  if (SkipBody) {
+    // skipped
+  } else if (!Body) {
     SynthesizeImplicitFunctionBody(GD, Fn, Args);
-  else {
+  else {
     if (isa<CXXTryStmt>(Body))
       OuterTryBlock = cast<CXXTryStmt>(Body);
-
     EmitStmt(Body);
   }
 
-  // Emit special ctor/ctor epilogues.
+  // Emit special ctor/dtor epilogues.
   if (isa<CXXConstructorDecl>(FD)) {
-    // If any of the member initializers are temporaries bound to references
-    // make sure to emit their destructors.
+    // Be sure to emit any cleanup blocks associated with the member
+    // or base initializers, which includes (along the exceptional
+    // path) the destructors for those members and bases that were
+    // fully constructed.
     EmitCleanupBlocks(0);
+
   } else if (const CXXDestructorDecl *DD = dyn_cast<CXXDestructorDecl>(FD)) {
+    // Funnel the previously-pushed cleanup block into the epilogue.
     CleanupBlockInfo Info = PopCleanupBlock();
-    assert(Info.CleanupBlock == DtorEpilogue && "Block mismatch!");
+    EmitBlock(Info.CleanupBlock);
 
-    EmitBlock(DtorEpilogue);
     EmitDtorEpilogue(DD, GD.getDtorType());
-      
+
+    // Go ahead and link in the switch and end blocks.
     if (Info.SwitchBlock)
       EmitBlock(Info.SwitchBlock);
     if (Info.EndBlock)
index 1ee598afdc3f4a912f7e46f75a028078f964e8d9..b6e85e208b154671199076f6c70fc318ccd7e6a7 100644 (file)
@@ -16,4 +16,36 @@ basic_iostream<char> res;
 int main() {
 }
 
-// CHECK: call void @_ZN9basic_iosD2Ev
+// basic_iostream's complete dtor calls its base dtor, then its
+// virtual base's dtor.
+//  CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED1Ev
+//  CHECK: call void @_ZN14basic_iostreamIcED2Ev
+//  CHECK: call void @_ZN9basic_iosD2Ev
+
+// basic_iostream's deleting dtor calls its complete dtor, then
+// operator delete().
+//  CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED0Ev
+//  CHECK: call void @_ZN14basic_iostreamIcED1Ev
+//  CHECK: call void @_ZdlPv
+
+// basic_istream's complete dtor calls the base dtor,
+// then its virtual base's base dtor.
+//  CHECK: define linkonce_odr void @_ZN13basic_istreamIcED1Ev
+//  CHECK: call void @_ZN13basic_istreamIcED2Ev
+//  CHECK: call void @_ZN9basic_iosD2Ev
+
+// basic_istream's deleting dtor calls the complete dtor, then
+// operator delete().
+//  CHECK: define linkonce_odr void @_ZN13basic_istreamIcED0Ev
+//  CHECK: call void @_ZN13basic_istreamIcED1Ev
+//  CHECK: call void @_ZdlPv
+
+// basic_iostream's base dtor calls its non-virtual base dtor.
+//  CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED2Ev
+//  CHECK: call void @_ZN13basic_istreamIcED2Ev
+//  CHECK: }
+
+// basic_istream's base dtor is a no-op.
+//  CHECK: define linkonce_odr void @_ZN13basic_istreamIcED2Ev
+//  CHECK-NOT: call
+//  CHECK: }
index 650fd69bbe7d5f6cb3c7facd498953759e1f2acf..8fe10476d4ae4dcb0f66265bd427865914667845 100644 (file)
@@ -8,15 +8,15 @@ struct B : A {
   virtual ~B();
 };
 
-// Complete dtor.
+// Complete dtor: just an alias because there are no virtual bases.
 // CHECK: @_ZN1BD1Ev = alias {{.*}} @_ZN1BD2Ev
 
-// Deleting dtor.
+// Deleting dtor: defers to the complete dtor.
 // CHECK: define void @_ZN1BD0Ev
-// CHECK: call void @_ZN1AD2Ev
-// check: call void @_ZdlPv
+// CHECK: call void @_ZN1BD1Ev
+// CHECK: call void @_ZdlPv
 
-// Base dtor.
+// Base dtor: actually calls A's base dtor.
 // CHECK: define void @_ZN1BD2Ev
 // CHECK: call void @_ZN1AD2Ev