]> granicus.if.org Git - clang/commitdiff
When performing a ::delete of an object with a virtual destructor,
authorJohn McCall <rjmccall@apple.com>
Tue, 25 Sep 2012 10:10:39 +0000 (10:10 +0000)
committerJohn McCall <rjmccall@apple.com>
Tue, 25 Sep 2012 10:10:39 +0000 (10:10 +0000)
be sure to delete the complete object pointer, not the original
pointer.  This is necessary if the base being deleted is at a
non-zero offset in the complete object.  This is only required
for objects with virtual destructors because deleting an object
via a base-class subobject when the base does not have a virtual
destructor is undefined behavior.

Noticed while reviewing the last four years of cxx-abi-dev
activity.

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

lib/CodeGen/CGCXXABI.h
lib/CodeGen/CGExprCXX.cpp
lib/CodeGen/ItaniumCXXABI.cpp
lib/CodeGen/MicrosoftCXXABI.cpp
test/CodeGenCXX/delete.cpp

index a0dcdfdcc6864282304be9cdef7f9af293051e8d..ffe70be2ca5424db3e3e3429e979b40ad9969651 100644 (file)
@@ -154,6 +154,15 @@ protected:
   llvm::Constant *getMemberPointerAdjustment(const CastExpr *E);
 
 public:
+  /// Adjust the given non-null pointer to an object of polymorphic
+  /// type to point to the complete object.
+  ///
+  /// The IR type of the result should be a pointer but is otherwise
+  /// irrelevant.
+  virtual llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
+                                              llvm::Value *ptr,
+                                              QualType type) = 0;
+
   /// Build the signature of the given constructor variant by adding
   /// any required parameters.  For convenience, ResTy has been
   /// initialized to 'void', and ArgTys has been initialized with the
index 3aa5e747812d6f828dbea027c75f3301b801feb3..7276440a47a81c3dde1868b2fcd1aed14777dd39 100644 (file)
@@ -1382,8 +1382,14 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
         if (UseGlobalDelete) {
           // If we're supposed to call the global delete, make sure we do so
           // even if the destructor throws.
+
+          // Derive the complete-object pointer, which is what we need
+          // to pass to the deallocation function.
+          llvm::Value *completePtr =
+            CGF.CGM.getCXXABI().adjustToCompleteObject(CGF, Ptr, ElementType);
+
           CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup,
-                                                    Ptr, OperatorDelete, 
+                                                    completePtr, OperatorDelete,
                                                     ElementType);
         }
         
index 5fbff3c253dfc361e1d2708360a8c9765e5de4ba..877d0d4ae21d42f372bf7be94881f9e31cc58419 100644 (file)
@@ -92,6 +92,10 @@ public:
                                           llvm::Value *Addr,
                                           const MemberPointerType *MPT);
 
+  llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
+                                      llvm::Value *ptr,
+                                      QualType type);
+
   void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
                                  CXXCtorType T,
                                  CanQualType &ResTy,
@@ -677,6 +681,25 @@ bool ItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
   return MPT->getPointeeType()->isFunctionType();
 }
 
+/// The Itanium ABI always places an offset to the complete object
+/// at entry -2 in the vtable.
+llvm::Value *ItaniumCXXABI::adjustToCompleteObject(CodeGenFunction &CGF,
+                                                   llvm::Value *ptr,
+                                                   QualType type) {
+  // Grab the vtable pointer as an intptr_t*.
+  llvm::Value *vtable = CGF.GetVTablePtr(ptr, CGF.IntPtrTy->getPointerTo());
+
+  // Track back to entry -2 and pull out the offset there.
+  llvm::Value *offsetPtr = 
+    CGF.Builder.CreateConstInBoundsGEP1_64(vtable, -2, "complete-offset.ptr");
+  llvm::LoadInst *offset = CGF.Builder.CreateLoad(offsetPtr);
+  offset->setAlignment(CGF.PointerAlignInBytes);
+
+  // Apply the offset.
+  ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy);
+  return CGF.Builder.CreateInBoundsGEP(ptr, offset);
+}
+
 /// The generic ABI passes 'this', plus a VTT if it's initializing a
 /// base subobject.
 void ItaniumCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor,
index b000c68a952cd028f14f090be75e36e04e763826..2c8173ba38419f8396db0a3042188356de021fcb 100644 (file)
@@ -30,6 +30,10 @@ public:
 
   StringRef GetPureVirtualCallName() { return "_purecall"; }
 
+  llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
+                                      llvm::Value *ptr,
+                                      QualType type);
+
   void BuildConstructorSignature(const CXXConstructorDecl *Ctor,
                                  CXXCtorType Type,
                                  CanQualType &ResTy,
@@ -95,6 +99,13 @@ public:
 
 }
 
+llvm::Value *MicrosoftCXXABI::adjustToCompleteObject(CodeGenFunction &CGF,
+                                                     llvm::Value *ptr,
+                                                     QualType type) {
+  // FIXME: implement
+  return ptr;
+}
+
 bool MicrosoftCXXABI::needThisReturn(GlobalDecl GD) {
   const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
   return isa<CXXConstructorDecl>(MD);
index 5a88f9f92437376a9424fffcc6fed85bcc87840c..7a91ca814637f148a74314c90a2ed460b2a5f55d 100644 (file)
@@ -113,12 +113,23 @@ namespace test4 {
 
   // CHECK: define void @_ZN5test421global_delete_virtualEPNS_1XE
   void global_delete_virtual(X *xp) {
-    // CHECK: [[VTABLE:%.*]] = load void ([[X:%.*]])***
-    // CHECK-NEXT: [[VFN:%.*]] = getelementptr inbounds void ([[X]])** [[VTABLE]], i64 0
-    // CHECK-NEXT: [[VFNPTR:%.*]] = load void ([[X]])** [[VFN]]
-    // CHECK-NEXT: call void [[VFNPTR]]([[X]] [[OBJ:%.*]])
-    // CHECK-NEXT: [[OBJVOID:%.*]] = bitcast [[X]] [[OBJ]] to i8*
-    // CHECK-NEXT: call void @_ZdlPv(i8* [[OBJVOID]]) nounwind
+    //   Load the offset-to-top from the vtable and apply it.
+    //   This has to be done first because the dtor can mess it up.
+    // CHECK:      [[T0:%.*]] = bitcast [[X:%.*]]* [[XP:%.*]] to i64**
+    // CHECK-NEXT: [[VTABLE:%.*]] = load i64** [[T0]]
+    // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds i64* [[VTABLE]], i64 -2
+    // CHECK-NEXT: [[OFFSET:%.*]] = load i64* [[T0]], align 8
+    // CHECK-NEXT: [[T0:%.*]] = bitcast [[X]]* [[XP]] to i8*
+    // CHECK-NEXT: [[ALLOCATED:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]]
+    //   Load the complete-object destructor (not the deleting destructor)
+    //   and call it.
+    // CHECK-NEXT: [[T0:%.*]] = bitcast [[X:%.*]]* [[XP:%.*]] to void ([[X]]*)***
+    // CHECK-NEXT: [[VTABLE:%.*]] = load void ([[X]]*)*** [[T0]]
+    // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds void ([[X]]*)** [[VTABLE]], i64 0
+    // CHECK-NEXT: [[DTOR:%.*]] = load void ([[X]]*)** [[T0]]
+    // CHECK-NEXT: call void [[DTOR]]([[X]]* [[OBJ:%.*]])
+    //   Call the global operator delete.
+    // CHECK-NEXT: call void @_ZdlPv(i8* [[ALLOCATED]]) nounwind
     ::delete xp;
   }
 }