forked from OSchip/llvm-project
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:
parent
8b907e8acb
commit
82fb892019
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue