From ecd03b447bb0e2ed1954c77441d49a4a17ca8138 Mon Sep 17 00:00:00 2001 From: John McCall Date: Tue, 25 Sep 2012 10:10:39 +0000 Subject: [PATCH] When performing a ::delete of an object with a virtual destructor, 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 | 9 +++++++++ lib/CodeGen/CGExprCXX.cpp | 8 +++++++- lib/CodeGen/ItaniumCXXABI.cpp | 23 +++++++++++++++++++++++ lib/CodeGen/MicrosoftCXXABI.cpp | 11 +++++++++++ test/CodeGenCXX/delete.cpp | 23 +++++++++++++++++------ 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index a0dcdfdcc6..ffe70be2ca 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -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 diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 3aa5e74781..7276440a47 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -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(NormalAndEHCleanup, - Ptr, OperatorDelete, + completePtr, OperatorDelete, ElementType); } diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 5fbff3c253..877d0d4ae2 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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, diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index b000c68a95..2c8173ba38 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -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(GD.getDecl()); return isa(MD); diff --git a/test/CodeGenCXX/delete.cpp b/test/CodeGenCXX/delete.cpp index 5a88f9f924..7a91ca8146 100644 --- a/test/CodeGenCXX/delete.cpp +++ b/test/CodeGenCXX/delete.cpp @@ -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; } } -- 2.40.0