MS ABI: Properly call global delete when invoking virtual destructors

Summary:
The Itanium ABI approach of using offset-to-top isn't possible with the
MS ABI, it doesn't have that kind of information lying around.

Instead, we do the following:
- Call the virtual deleting destructor with the "don't delete the object
  flag" set.  The virtual deleting destructor will return a pointer to
  'this' adjusted to the most derived class.
- Call the global delete using the adjusted 'this' pointer.

Reviewers: rnk

Subscribers: cfe-commits

Differential Revision: http://reviews.llvm.org/D5996

llvm-svn: 220993
This commit is contained in:
David Majnemer 2014-10-31 20:09:12 +00:00
parent 6e21338abb
commit 0c0b6d9ac6
12 changed files with 227 additions and 148 deletions

View File

@ -93,6 +93,8 @@ public:
/// when called virtually, and code generation does not support the case.
virtual bool HasThisReturn(GlobalDecl GD) const { return false; }
virtual bool hasMostDerivedReturn(GlobalDecl GD) const { return false; }
/// If the C++ ABI requires the given type be returned in a particular way,
/// this method sets RetAI and returns true.
virtual bool classifyReturnType(CGFunctionInfo &FI) const = 0;
@ -207,14 +209,11 @@ protected:
CharUnits getMemberPointerPathAdjustment(const APValue &MP);
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;
virtual void emitVirtualObjectDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
llvm::Value *Ptr, QualType ElementType,
bool UseGlobalDelete,
const CXXDestructorDecl *Dtor) = 0;
virtual llvm::Constant *getAddrOfRTTIDescriptor(QualType Ty) = 0;
@ -359,11 +358,10 @@ public:
llvm::Type *Ty) = 0;
/// Emit the ABI-specific virtual destructor call.
virtual void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
llvm::Value *This,
const CXXMemberCallExpr *CE) = 0;
virtual llvm::Value *
EmitVirtualDestructorCall(CodeGenFunction &CGF, const CXXDestructorDecl *Dtor,
CXXDtorType DtorType, llvm::Value *This,
const CXXMemberCallExpr *CE) = 0;
virtual void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF,
GlobalDecl GD,

View File

@ -214,8 +214,11 @@ CodeGenTypes::arrangeCXXStructorDeclaration(const CXXMethodDecl *MD,
(MD->isVariadic() ? RequiredArgs(argTypes.size()) : RequiredArgs::All);
FunctionType::ExtInfo extInfo = FTP->getExtInfo();
CanQualType resultType =
TheCXXABI.HasThisReturn(GD) ? argTypes.front() : Context.VoidTy;
CanQualType resultType = TheCXXABI.HasThisReturn(GD)
? argTypes.front()
: TheCXXABI.hasMostDerivedReturn(GD)
? CGM.getContext().VoidPtrTy
: Context.VoidTy;
return arrangeLLVMFunctionInfo(resultType, true, argTypes, extInfo, required);
}
@ -233,8 +236,11 @@ CodeGenTypes::arrangeCXXConstructorCall(const CallArgList &args,
CanQual<FunctionProtoType> FPT = GetFormalType(D);
RequiredArgs Required = RequiredArgs::forPrototypePlus(FPT, 1 + ExtraArgs);
GlobalDecl GD(D, CtorKind);
CanQualType ResultType =
TheCXXABI.HasThisReturn(GD) ? ArgTypes.front() : Context.VoidTy;
CanQualType ResultType = TheCXXABI.HasThisReturn(GD)
? ArgTypes.front()
: TheCXXABI.hasMostDerivedReturn(GD)
? CGM.getContext().VoidPtrTy
: Context.VoidTy;
FunctionType::ExtInfo Info = FPT->getExtInfo();
return arrangeLLVMFunctionInfo(ResultType, true, ArgTypes, Info, Required);

View File

@ -24,10 +24,10 @@
using namespace clang;
using namespace CodeGen;
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
const CallExpr *CE) {
static RequiredArgs commonEmitCXXMemberOrOperatorCall(
CodeGenFunction &CGF, const CXXMethodDecl *MD, llvm::Value *Callee,
ReturnValueSlot ReturnValue, llvm::Value *This, llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *CE, CallArgList &Args) {
assert(CE == nullptr || isa<CXXMemberCallExpr>(CE) ||
isa<CXXOperatorCallExpr>(CE));
assert(MD->isInstance() &&
@ -39,14 +39,13 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
SourceLocation CallLoc;
if (CE)
CallLoc = CE->getExprLoc();
EmitTypeCheck(isa<CXXConstructorDecl>(MD) ? TCK_ConstructorCall
: TCK_MemberCall,
CallLoc, This, getContext().getRecordType(MD->getParent()));
CallArgList Args;
CGF.EmitTypeCheck(
isa<CXXConstructorDecl>(MD) ? CodeGenFunction::TCK_ConstructorCall
: CodeGenFunction::TCK_MemberCall,
CallLoc, This, CGF.getContext().getRecordType(MD->getParent()));
// Push the this ptr.
Args.add(RValue::get(This), MD->getThisType(getContext()));
Args.add(RValue::get(This), MD->getThisType(CGF.getContext()));
// If there is an implicit parameter (e.g. VTT), emit it.
if (ImplicitParam) {
@ -60,18 +59,40 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
if (CE) {
// Special case: skip first argument of CXXOperatorCall (it is "this").
unsigned ArgsToSkip = isa<CXXOperatorCallExpr>(CE) ? 1 : 0;
EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(),
CE->getDirectCallee());
CGF.EmitCallArgs(Args, FPT, CE->arg_begin() + ArgsToSkip, CE->arg_end(),
CE->getDirectCallee());
} else {
assert(
FPT->getNumParams() == 0 &&
"No CallExpr specified for function with non-zero number of arguments");
}
return required;
}
RValue CodeGenFunction::EmitCXXMemberOrOperatorCall(
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
const CallExpr *CE) {
const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>();
CallArgList Args;
RequiredArgs required = commonEmitCXXMemberOrOperatorCall(
*this, MD, Callee, ReturnValue, This, ImplicitParam, ImplicitParamTy, CE,
Args);
return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required),
Callee, ReturnValue, Args, MD);
}
RValue CodeGenFunction::EmitCXXStructorCall(
const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue,
llvm::Value *This, llvm::Value *ImplicitParam, QualType ImplicitParamTy,
const CallExpr *CE, StructorType Type) {
CallArgList Args;
commonEmitCXXMemberOrOperatorCall(*this, MD, Callee, ReturnValue, This,
ImplicitParam, ImplicitParamTy, CE, Args);
return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(MD, Type),
Callee, ReturnValue, Args, MD);
}
static CXXRecordDecl *getCXXRecord(const Expr *E) {
QualType T = E->getType();
if (const PointerType *PTy = T->getAs<PointerType>())
@ -1389,6 +1410,14 @@ namespace {
};
}
void
CodeGenFunction::pushCallObjectDeleteCleanup(const FunctionDecl *OperatorDelete,
llvm::Value *CompletePtr,
QualType ElementType) {
EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup, CompletePtr,
OperatorDelete, ElementType);
}
/// Emit the code for deleting a single object.
static void EmitObjectDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
@ -1404,30 +1433,8 @@ static void EmitObjectDelete(CodeGenFunction &CGF,
Dtor = RD->getDestructor();
if (Dtor->isVirtual()) {
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,
completePtr, OperatorDelete,
ElementType);
}
// FIXME: Provide a source location here even though there's no
// CXXMemberCallExpr for dtor call.
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
CGF.CGM.getCXXABI().EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr,
nullptr);
if (UseGlobalDelete) {
CGF.PopCleanupBlock();
}
CGF.CGM.getCXXABI().emitVirtualObjectDelete(
CGF, OperatorDelete, Ptr, ElementType, UseGlobalDelete, Dtor);
return;
}
}

View File

@ -200,8 +200,11 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD,
const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl());
QualType ThisType = MD->getThisType(getContext());
const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
QualType ResultType =
CGM.getCXXABI().HasThisReturn(GD) ? ThisType : FPT->getReturnType();
QualType ResultType = CGM.getCXXABI().HasThisReturn(GD)
? ThisType
: CGM.getCXXABI().hasMostDerivedReturn(GD)
? CGM.getContext().VoidPtrTy
: FPT->getReturnType();
FunctionArgList FunctionArgs;
// Create the implicit 'this' parameter declaration.
@ -278,8 +281,11 @@ void CodeGenFunction::EmitCallAndReturnForThunk(llvm::Value *Callee,
#endif
// Determine whether we have a return value slot to use.
QualType ResultType =
CGM.getCXXABI().HasThisReturn(CurGD) ? ThisType : FPT->getReturnType();
QualType ResultType = CGM.getCXXABI().HasThisReturn(CurGD)
? ThisType
: CGM.getCXXABI().hasMostDerivedReturn(CurGD)
? CGM.getContext().VoidPtrTy
: FPT->getReturnType();
ReturnValueSlot Slot;
if (!ResultType->isVoidType() &&
CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&

View File

@ -822,6 +822,8 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
if (MD && MD->isInstance()) {
if (CGM.getCXXABI().HasThisReturn(GD))
ResTy = MD->getThisType(getContext());
else if (CGM.getCXXABI().hasMostDerivedReturn(GD))
ResTy = CGM.getContext().VoidPtrTy;
CGM.getCXXABI().buildThisParam(*this, Args);
}

View File

@ -1136,6 +1136,9 @@ public:
void pushLifetimeExtendedDestroy(CleanupKind kind, llvm::Value *addr,
QualType type, Destroyer *destroyer,
bool useEHCleanupForArray);
void pushCallObjectDeleteCleanup(const FunctionDecl *OperatorDelete,
llvm::Value *CompletePtr,
QualType ElementType);
void pushStackRestore(CleanupKind kind, llvm::Value *SPMem);
void emitDestroy(llvm::Value *addr, QualType type, Destroyer *destroyer,
bool useEHCleanupForArray);
@ -2319,6 +2322,11 @@ public:
ReturnValueSlot ReturnValue, llvm::Value *This,
llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *E);
RValue EmitCXXStructorCall(const CXXMethodDecl *MD, llvm::Value *Callee,
ReturnValueSlot ReturnValue, llvm::Value *This,
llvm::Value *ImplicitParam,
QualType ImplicitParamTy, const CallExpr *E,
StructorType Type);
RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E,
ReturnValueSlot ReturnValue);
RValue EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E,

View File

@ -106,8 +106,11 @@ public:
llvm::Value *Addr,
const MemberPointerType *MPT) override;
llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF, llvm::Value *ptr,
QualType type) override;
void emitVirtualObjectDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
llvm::Value *Ptr, QualType ElementType,
bool UseGlobalDelete,
const CXXDestructorDecl *Dtor) override;
void EmitFundamentalRTTIDescriptor(QualType Type);
void EmitFundamentalRTTIDescriptors();
@ -187,10 +190,11 @@ public:
llvm::Value *This,
llvm::Type *Ty) override;
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType, llvm::Value *This,
const CXXMemberCallExpr *CE) override;
llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
llvm::Value *This,
const CXXMemberCallExpr *CE) override;
void emitVirtualInheritanceTables(const CXXRecordDecl *RD) override;
@ -847,21 +851,38 @@ bool ItaniumCXXABI::isZeroInitializable(const MemberPointerType *MPT) {
/// 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());
void ItaniumCXXABI::emitVirtualObjectDelete(
CodeGenFunction &CGF, const FunctionDecl *OperatorDelete, llvm::Value *Ptr,
QualType ElementType, bool UseGlobalDelete, const CXXDestructorDecl *Dtor) {
if (UseGlobalDelete) {
// Derive the complete-object pointer, which is what we need
// to pass to the deallocation function.
// 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);
// Grab the vtable pointer as an intptr_t*.
llvm::Value *VTable = CGF.GetVTablePtr(Ptr, CGF.IntPtrTy->getPointerTo());
// Apply the offset.
ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy);
return CGF.Builder.CreateInBoundsGEP(ptr, offset);
// 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.
llvm::Value *CompletePtr = CGF.Builder.CreateBitCast(Ptr, CGF.Int8PtrTy);
CompletePtr = CGF.Builder.CreateInBoundsGEP(CompletePtr, Offset);
// If we're supposed to call the global delete, make sure we do so
// even if the destructor throws.
CGF.pushCallObjectDeleteCleanup(OperatorDelete, CompletePtr, ElementType);
}
// FIXME: Provide a source location here even though there's no
// CXXMemberCallExpr for dtor call.
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr);
if (UseGlobalDelete)
CGF.PopCleanupBlock();
}
static llvm::Constant *getItaniumDynamicCastFn(CodeGenFunction &CGF) {
@ -1333,11 +1354,9 @@ llvm::Value *ItaniumCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
return CGF.Builder.CreateLoad(VFuncPtr);
}
void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
llvm::Value *This,
const CXXMemberCallExpr *CE) {
llvm::Value *ItaniumCXXABI::EmitVirtualDestructorCall(
CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
llvm::Value *This, const CXXMemberCallExpr *CE) {
assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
@ -1349,6 +1368,7 @@ void ItaniumCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This,
/*ImplicitParam=*/nullptr, QualType(), CE);
return nullptr;
}
void ItaniumCXXABI::emitVirtualInheritanceTables(const CXXRecordDecl *RD) {

View File

@ -43,6 +43,7 @@ public:
CompleteObjectLocatorType(nullptr) {}
bool HasThisReturn(GlobalDecl GD) const override;
bool hasMostDerivedReturn(GlobalDecl GD) const override;
bool classifyReturnType(CGFunctionInfo &FI) const override;
@ -65,9 +66,11 @@ public:
StringRef GetPureVirtualCallName() override { return "_purecall"; }
StringRef GetDeletedVirtualCallName() override { return "_purecall"; }
llvm::Value *adjustToCompleteObject(CodeGenFunction &CGF,
llvm::Value *ptr,
QualType type) override;
void emitVirtualObjectDelete(CodeGenFunction &CGF,
const FunctionDecl *OperatorDelete,
llvm::Value *Ptr, QualType ElementType,
bool UseGlobalDelete,
const CXXDestructorDecl *Dtor) override;
llvm::GlobalVariable *getMSCompleteObjectLocator(const CXXRecordDecl *RD,
const VPtrInfo *Info);
@ -211,10 +214,11 @@ public:
llvm::Value *This,
llvm::Type *Ty) override;
void EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType, llvm::Value *This,
const CXXMemberCallExpr *CE) override;
llvm::Value *EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
llvm::Value *This,
const CXXMemberCallExpr *CE) override;
void adjustCallArgsForDestructorThunk(CodeGenFunction &CGF, GlobalDecl GD,
CallArgList &CallArgs) override {
@ -641,11 +645,16 @@ MicrosoftCXXABI::getRecordArgABI(const CXXRecordDecl *RD) const {
llvm_unreachable("invalid enum");
}
llvm::Value *MicrosoftCXXABI::adjustToCompleteObject(CodeGenFunction &CGF,
llvm::Value *ptr,
QualType type) {
// FIXME: implement
return ptr;
void MicrosoftCXXABI::emitVirtualObjectDelete(
CodeGenFunction &CGF, const FunctionDecl *OperatorDelete, llvm::Value *Ptr,
QualType ElementType, bool UseGlobalDelete, const CXXDestructorDecl *Dtor) {
// FIXME: Provide a source location here even though there's no
// CXXMemberCallExpr for dtor call.
CXXDtorType DtorType = UseGlobalDelete ? Dtor_Complete : Dtor_Deleting;
llvm::Value *MDThis =
EmitVirtualDestructorCall(CGF, Dtor, DtorType, Ptr, /*CE=*/nullptr);
if (UseGlobalDelete)
CGF.EmitDeleteCall(OperatorDelete, MDThis, ElementType);
}
/// \brief Gets the offset to the virtual base that contains the vfptr for
@ -795,6 +804,15 @@ bool MicrosoftCXXABI::HasThisReturn(GlobalDecl GD) const {
return isa<CXXConstructorDecl>(GD.getDecl());
}
static bool isDeletingDtor(GlobalDecl GD) {
return isa<CXXDestructorDecl>(GD.getDecl()) &&
GD.getDtorType() == Dtor_Deleting;
}
bool MicrosoftCXXABI::hasMostDerivedReturn(GlobalDecl GD) const {
return isDeletingDtor(GD);
}
bool MicrosoftCXXABI::classifyReturnType(CGFunctionInfo &FI) const {
const CXXRecordDecl *RD = FI.getReturnType()->getAsCXXRecordDecl();
if (!RD)
@ -1059,14 +1077,6 @@ llvm::Value *MicrosoftCXXABI::adjustThisArgumentForVirtualFunctionCall(
return This;
}
static bool IsDeletingDtor(GlobalDecl GD) {
const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl());
if (isa<CXXDestructorDecl>(MD)) {
return GD.getDtorType() == Dtor_Deleting;
}
return false;
}
void MicrosoftCXXABI::addImplicitStructorParams(CodeGenFunction &CGF,
QualType &ResTy,
FunctionArgList &Params) {
@ -1087,7 +1097,7 @@ void MicrosoftCXXABI::addImplicitStructorParams(CodeGenFunction &CGF,
else
Params.push_back(IsMostDerived);
getStructorImplicitParamDecl(CGF) = IsMostDerived;
} else if (IsDeletingDtor(CGF.CurGD)) {
} else if (isDeletingDtor(CGF.CurGD)) {
ImplicitParamDecl *ShouldDelete
= ImplicitParamDecl::Create(Context, nullptr,
CGF.CurGD.getDecl()->getLocation(),
@ -1133,6 +1143,9 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
/// HasThisReturn only specifies a contract, not the implementation
if (HasThisReturn(CGF.CurGD))
CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue);
else if (hasMostDerivedReturn(CGF.CurGD))
CGF.Builder.CreateStore(CGF.EmitCastToVoidPtr(getThisValue(CGF)),
CGF.ReturnValue);
const CXXMethodDecl *MD = cast<CXXMethodDecl>(CGF.CurGD.getDecl());
if (isa<CXXConstructorDecl>(MD) && MD->getParent()->getNumVBases()) {
@ -1144,7 +1157,7 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) {
"is_most_derived");
}
if (IsDeletingDtor(CGF.CurGD)) {
if (isDeletingDtor(CGF.CurGD)) {
assert(getStructorImplicitParamDecl(CGF) &&
"no implicit parameter for a deleting destructor?");
getStructorImplicitParamValue(CGF)
@ -1192,9 +1205,10 @@ void MicrosoftCXXABI::EmitDestructorCall(CodeGenFunction &CGF,
This, false);
}
CGF.EmitCXXMemberOrOperatorCall(DD, Callee, ReturnValueSlot(), This,
/*ImplicitParam=*/nullptr,
/*ImplicitParamTy=*/QualType(), nullptr);
CGF.EmitCXXStructorCall(DD, Callee, ReturnValueSlot(), This,
/*ImplicitParam=*/nullptr,
/*ImplicitParamTy=*/QualType(), nullptr,
getFromDtorType(Type));
}
void MicrosoftCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
@ -1402,11 +1416,9 @@ llvm::Value *MicrosoftCXXABI::getVirtualFunctionPointer(CodeGenFunction &CGF,
return Builder.CreateLoad(VFuncPtr);
}
void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
const CXXDestructorDecl *Dtor,
CXXDtorType DtorType,
llvm::Value *This,
const CXXMemberCallExpr *CE) {
llvm::Value *MicrosoftCXXABI::EmitVirtualDestructorCall(
CodeGenFunction &CGF, const CXXDestructorDecl *Dtor, CXXDtorType DtorType,
llvm::Value *This, const CXXMemberCallExpr *CE) {
assert(CE == nullptr || CE->arg_begin() == CE->arg_end());
assert(DtorType == Dtor_Deleting || DtorType == Dtor_Complete);
@ -1424,8 +1436,10 @@ void MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF,
DtorType == Dtor_Deleting);
This = adjustThisArgumentForVirtualFunctionCall(CGF, GD, This, true);
CGF.EmitCXXMemberOrOperatorCall(Dtor, Callee, ReturnValueSlot(), This,
ImplicitParam, Context.IntTy, CE);
RValue RV = CGF.EmitCXXStructorCall(Dtor, Callee, ReturnValueSlot(), This,
ImplicitParam, Context.IntTy, CE,
StructorType::Deleting);
return RV.getScalarVal();
}
const VBTableGlobals &

View File

@ -534,7 +534,7 @@ struct __declspec(dllexport) Y {
struct __declspec(dllexport) Z { virtual ~Z() {} };
// The scalar deleting dtor does not get exported:
// M32-DAG: define linkonce_odr x86_thiscallcc void @"\01??_GZ@@UAEPAXI@Z"
// M32-DAG: define linkonce_odr x86_thiscallcc i8* @"\01??_GZ@@UAEPAXI@Z"
// The user-defined dtor does get exported:

View File

@ -46,8 +46,9 @@ B::B() {
struct C {
virtual ~C() {
// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %should_call_delete)
// DTORS: define linkonce_odr x86_thiscallcc i8* @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %should_call_delete)
// DTORS: store i32 %should_call_delete, i32* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 4
// DTORS: store i8* %{{.*}}, i8** %[[RETVAL:[0-9a-z._]+]]
// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32* %[[SHOULD_DELETE_VAR]]
// DTORS: call x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]])
// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0
@ -59,7 +60,8 @@ struct C {
// DTORS-NEXT: br label %[[CONTINUE_LABEL]]
//
// DTORS: [[CONTINUE_LABEL]]
// DTORS-NEXT: ret void
// DTORS-NEXT: %[[RET:.*]] = load i8** %[[RETVAL]]
// DTORS-NEXT: ret i8* %[[RET]]
// Check that we do the mangling correctly on x64.
// DTORS-X64: @"\01??_GC@basic@@UEAAPEAXI@Z"
@ -81,11 +83,11 @@ void call_complete_dtor(C *obj_ptr) {
// CHECK: define void @"\01?call_complete_dtor@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
obj_ptr->~C();
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
// CHECK-NEXT: ret void
}
@ -96,11 +98,27 @@ void call_deleting_dtor(C *obj_ptr) {
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
// CHECK: [[DELETE_NOTNULL]]
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to void (%"struct.basic::C"*, i32)***
// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i32)** %[[PVDTOR]]
// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
// CHECK: ret void
}
void call_deleting_dtor_and_global_delete(C *obj_ptr) {
// CHECK: define void @"\01?call_deleting_dtor_and_global_delete@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
::delete obj_ptr;
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
// CHECK: [[DELETE_NOTNULL]]
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
// CHECK-NEXT: %[[CALL:.*]] = call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
// CHECK-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[CALL]])
// CHECK: ret void
}
@ -153,13 +171,13 @@ C::~C() {
void foo() {
C c;
}
// DTORS2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
// DTORS2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
// DTORS2: (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete)
// Do an adjustment from B* to C*.
// DTORS2: getelementptr i8* %{{.*}}, i32 -4
// DTORS2: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"*
// DTORS2: call x86_thiscallcc void @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
// DTORS2: ret void
// DTORS2: %[[CALL:.*]] = call x86_thiscallcc i8* @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
// DTORS2: ret i8* %[[CALL]]
}
@ -436,7 +454,7 @@ struct A {
void *getA() {
return (void*)new A();
}
// CHECK: define internal x86_thiscallcc void @"\01??_GA@?A@@UAEPAXI@Z"
// CHECK: define internal x86_thiscallcc i8* @"\01??_GA@?A@@UAEPAXI@Z"
// CHECK: (%"struct.(anonymous namespace)::A"* %this, i32 %should_call_delete)
// CHECK: define internal x86_thiscallcc void @"\01??1A@?A@@UAE@XZ"
// CHECK: (%"struct.(anonymous namespace)::A"* %this)

View File

@ -61,10 +61,10 @@ struct C : A, B {
C::C() {} // Emits vftable and forces thunk generation.
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_EC@@W3AEPAXI@Z"(%struct.C* %this, i32 %should_call_delete)
// CODEGEN: getelementptr i8* {{.*}}, i32 -4
// FIXME: should actually call _EC, not _GC.
// CODEGEN: call x86_thiscallcc void @"\01??_GC@@UAEPAXI@Z"
// CODEGEN: call x86_thiscallcc i8* @"\01??_GC@@UAEPAXI@Z"
// CODEGEN: ret
// CODEGEN-LABEL: define linkonce_odr x86_thiscallcc void @"\01?public_f@C@@W3AEXXZ"(%struct.C*

View File

@ -96,7 +96,7 @@ B::~B() {
// CHECK2: call x86_thiscallcc void @"\01??1VBase@@UAE@XZ"(%struct.VBase* %[[VBASE]])
// CHECK2: ret
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_GB@@UAEPAXI@Z"
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_GB@@UAEPAXI@Z"
// CHECK2: %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* {{.*}} to i8*
// CHECK2: %[[THIS_i8:.*]] = getelementptr inbounds i8* %[[THIS_PARAM_i8:.*]], i32 -8
// CHECK2: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
@ -184,10 +184,10 @@ void delete_B(B *obj) {
// CHECK: %[[VBOFFSET32:.*]] = load i32* %[[VBENTRY]]
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to void (%struct.B*, i32)***
// CHECK: %[[VFTABLE:.*]] = load void (%struct.B*, i32)*** %[[VFPTR]]
// CHECK: %[[VFUN:.*]] = getelementptr inbounds void (%struct.B*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN_VALUE:.*]] = load void (%struct.B*, i32)** %[[VFUN]]
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (%struct.B*, i32)***
// CHECK: %[[VFTABLE:.*]] = load i8* (%struct.B*, i32)*** %[[VFPTR]]
// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (%struct.B*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN_VALUE:.*]] = load i8* (%struct.B*, i32)** %[[VFUN]]
//
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 0
@ -199,7 +199,7 @@ void delete_B(B *obj) {
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.B*
//
// CHECK: call x86_thiscallcc void %[[VFUN_VALUE]](%struct.B* %[[VBASE]], i32 1)
// CHECK: call x86_thiscallcc i8* %[[VFUN_VALUE]](%struct.B* %[[VBASE]], i32 1)
// CHECK: ret void
}
@ -407,11 +407,11 @@ void destroy(C *obj) {
// CHECK-LABEL: define void @"\01?destroy@test4@@YAXPAUC@1@@Z"(%"struct.test4::C"* %obj)
delete obj;
// CHECK: %[[VPTR:.*]] = bitcast %"struct.test4::C"* %[[OBJ:.*]] to void (%"struct.test4::C"*, i32)***
// CHECK: %[[VFTABLE:.*]] = load void (%"struct.test4::C"*, i32)*** %[[VPTR]]
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds void (%"struct.test4::C"*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN:.*]] = load void (%"struct.test4::C"*, i32)** %[[VFTENTRY]]
// CHECK: call x86_thiscallcc void %[[VFUN]](%"struct.test4::C"* %[[OBJ]], i32 1)
// CHECK: %[[VPTR:.*]] = bitcast %"struct.test4::C"* %[[OBJ:.*]] to i8* (%"struct.test4::C"*, i32)***
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::C"*, i32)*** %[[VPTR]]
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::C"*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::C"*, i32)** %[[VFTENTRY]]
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::C"* %[[OBJ]], i32 1)
// CHECK: ret
}
@ -442,15 +442,15 @@ void destroy(E *obj) {
// CHECK-NOT: getelementptr
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ:.*]] to i8*
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 4
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to void (%"struct.test4::E"*, i32)***
// CHECK: %[[VFTABLE:.*]] = load void (%"struct.test4::E"*, i32)*** %[[VPTR]]
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds void (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN:.*]] = load void (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (%"struct.test4::E"*, i32)***
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::E"*, i32)*** %[[VPTR]]
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ]] to i8*
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8* %[[OBJ_i8]], i32 4
// FIXME: in fact, the call should take i8* and the bitcast is redundant.
// CHECK: %[[B_as_E:.*]] = bitcast i8* %[[B_i8]] to %"struct.test4::E"*
// CHECK: call x86_thiscallcc void %[[VFUN]](%"struct.test4::E"* %[[B_as_E]], i32 1)
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::E"* %[[B_as_E]], i32 1)
delete obj;
}