From: David Majnemer Date: Fri, 31 Oct 2014 20:09:12 +0000 (+0000) Subject: MS ABI: Properly call global delete when invoking virtual destructors X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ce0076c36cb194e8b0872caf0895b4105625a70e;p=clang 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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@220993 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 9782ff3bf3..9739f06447 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -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, diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index ffa28c1769..b31cdf3a90 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -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 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); diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 483966fce2..f81edb6222 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -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(CE) || isa(CE)); assert(MD->isInstance() && @@ -39,14 +39,13 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( SourceLocation CallLoc; if (CE) CallLoc = CE->getExprLoc(); - EmitTypeCheck(isa(MD) ? TCK_ConstructorCall - : TCK_MemberCall, - CallLoc, This, getContext().getRecordType(MD->getParent())); - - CallArgList Args; + CGF.EmitTypeCheck( + isa(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(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(); + 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()) @@ -1389,6 +1410,14 @@ namespace { }; } +void +CodeGenFunction::pushCallObjectDeleteCleanup(const FunctionDecl *OperatorDelete, + llvm::Value *CompletePtr, + QualType ElementType) { + EHStack.pushCleanup(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(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; } } diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 51c29fe171..54f7eb06a1 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -200,8 +200,11 @@ void CodeGenFunction::StartThunk(llvm::Function *Fn, GlobalDecl GD, const CXXMethodDecl *MD = cast(GD.getDecl()); QualType ThisType = MD->getThisType(getContext()); const FunctionProtoType *FPT = MD->getType()->getAs(); - 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 && diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index e2bf11e037..16a0b4af09 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -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); } diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 5faf2acceb..2c7783234b 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -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, diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index eb5fbab438..93d5ff13ec 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -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()); - - // 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); +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. + + // 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. + 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); + } - // Apply the offset. - ptr = CGF.Builder.CreateBitCast(ptr, CGF.Int8PtrTy); - return CGF.Builder.CreateInBoundsGEP(ptr, offset); + // 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) { diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 9e5bbd130b..49907c965d 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -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(GD.getDecl()); } +static bool isDeletingDtor(GlobalDecl GD) { + return isa(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(GD.getDecl()); - if (isa(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(CGF.CurGD.getDecl()); if (isa(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 & diff --git a/test/CodeGenCXX/dllexport.cpp b/test/CodeGenCXX/dllexport.cpp index 0e6e989846..5eb2dbfa7f 100644 --- a/test/CodeGenCXX/dllexport.cpp +++ b/test/CodeGenCXX/dllexport.cpp @@ -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: diff --git a/test/CodeGenCXX/microsoft-abi-structors.cpp b/test/CodeGenCXX/microsoft-abi-structors.cpp index c8633527d6..0bc4c42674 100644 --- a/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -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) diff --git a/test/CodeGenCXX/microsoft-abi-thunks.cpp b/test/CodeGenCXX/microsoft-abi-thunks.cpp index f1602c8064..843bc89b0d 100644 --- a/test/CodeGenCXX/microsoft-abi-thunks.cpp +++ b/test/CodeGenCXX/microsoft-abi-thunks.cpp @@ -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* diff --git a/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp index 18d47cea48..c851f559f2 100644 --- a/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-virtual-inheritance.cpp @@ -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; }