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.

llvm-svn: 164597
This commit is contained in:
John McCall 2012-09-25 10:10:39 +00:00
parent 8b907e8acb
commit 82fb892019
5 changed files with 67 additions and 7 deletions

View 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

View 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);
}

View 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,

View 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);

View 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;
}
}