From: Timur Iskhodzhanov Date: Wed, 13 Feb 2013 08:37:51 +0000 (+0000) Subject: Emit virtual/deleting destructors properly with -cxx-abi microsoft, PR15058 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=59660c21178b6af518bd4b564e032d5c9cc218cb;p=clang Emit virtual/deleting destructors properly with -cxx-abi microsoft, PR15058 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@175045 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 326b0d3e3d..baa16b04ea 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -28,12 +28,25 @@ using namespace clang; namespace { +static const FunctionDecl *getStructor(const FunctionDecl *fn) { + if (const FunctionTemplateDecl *ftd = fn->getPrimaryTemplate()) + return ftd->getTemplatedDecl(); + + return fn; +} + /// MicrosoftCXXNameMangler - Manage the mangling of a single name for the /// Microsoft Visual C++ ABI. class MicrosoftCXXNameMangler { MangleContext &Context; raw_ostream &Out; + /// The "structor" is the top-level declaration being mangled, if + /// that's not a template specialization; otherwise it's the pattern + /// for that specialization. + const NamedDecl *Structor; + unsigned StructorType; + // FIXME: audit the performance of BackRefMap as it might do way too many // copying of strings. typedef std::map BackRefMap; @@ -47,7 +60,15 @@ class MicrosoftCXXNameMangler { public: MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_) - : Context(C), Out(Out_), UseNameBackReferences(true) { } + : Context(C), Out(Out_), + Structor(0), StructorType(-1), + UseNameBackReferences(true) { } + + MicrosoftCXXNameMangler(MangleContext &C, raw_ostream &Out_, + const CXXDestructorDecl *D, CXXDtorType Type) + : Context(C), Out(Out_), + Structor(getStructor(D)), StructorType(Type), + UseNameBackReferences(true) { } raw_ostream &getStream() const { return Out; } @@ -68,6 +89,7 @@ private: void mangleSourceName(const IdentifierInfo *II); void manglePostfix(const DeclContext *DC, bool NoFunction=false); void mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc); + void mangleCXXDtorType(CXXDtorType T); void mangleQualifiers(Qualifiers Quals, bool IsMember); void manglePointerQualifiers(Qualifiers Quals); @@ -485,7 +507,14 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, break; case DeclarationName::CXXDestructorName: - Out << "?1"; + if (ND == Structor) + // If the named decl is the C++ destructor we're mangling, + // use the type we were given. + mangleCXXDtorType(static_cast(StructorType)); + else + // Otherwise, use the complete destructor name. This is relevant if a + // class with a destructor is declared within a destructor. + mangleCXXDtorType(Dtor_Complete); break; case DeclarationName::CXXConversionFunctionName: @@ -543,6 +572,23 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, } } +void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) { + switch (T) { + case Dtor_Deleting: + Out << "?_G"; + return; + case Dtor_Base: + // FIXME: We should be asked to mangle base dtors. + // However, fixing this would require larger changes to the CodeGenModule. + // Please put llvm_unreachable here when CGM is changed. + // For now, just mangle a base dtor the same way as a complete dtor... + case Dtor_Complete: + Out << "?1"; + return; + } + llvm_unreachable("Unsupported dtor type?"); +} + void MicrosoftCXXNameMangler::mangleOperatorName(OverloadedOperatorKind OO, SourceLocation Loc) { switch (OO) { @@ -1110,9 +1156,18 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, // ::= // ::= @ # structors (they have no declared return type) - if (IsStructor) + if (IsStructor) { + if (isa(D) && D == Structor && + StructorType == Dtor_Deleting) { + // The scalar deleting destructor takes an extra int argument. + // However, the FunctionType generated has 0 arguments. + // FIXME: This is a temporary hack. + // Maybe should fix the FunctionType creation instead? + Out << "PAXI@Z"; + return; + } Out << '@'; - else { + } else { QualType Result = Proto->getResultType(); const Type* RT = Result.getTypePtr(); if (!RT->isAnyPointerType() && !RT->isReferenceType()) { @@ -1711,7 +1766,7 @@ void MicrosoftMangleContext::mangleCXXCtor(const CXXConstructorDecl *D, void MicrosoftMangleContext::mangleCXXDtor(const CXXDestructorDecl *D, CXXDtorType Type, raw_ostream & Out) { - MicrosoftCXXNameMangler mangler(*this, Out); + MicrosoftCXXNameMangler mangler(*this, Out, D, Type); mangler.mangle(D); } void MicrosoftMangleContext::mangleReferenceTemporary(const clang::VarDecl *VD, diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index 9a03faace2..f80232f44c 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -1160,6 +1160,8 @@ void VTableBuilder::ComputeThisAdjustments() { break; case VTableComponent::CK_DeletingDtorPointer: // We've already added the thunk when we saw the complete dtor pointer. + // FIXME: check how this works in the Microsoft ABI + // while working on the multiple inheritance patch. continue; } @@ -1302,11 +1304,8 @@ VTableBuilder::AddMethod(const CXXMethodDecl *MD, Components.push_back(VTableComponent::MakeCompleteDtor(DD)); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { - // Add only one destructor in MS mode. - // FIXME: The virtual destructors are handled differently in MS ABI, - // we should add such a support later. For now, put the complete - // destructor into the vftable just to make its layout right. - Components.push_back(VTableComponent::MakeCompleteDtor(DD)); + // Add the scalar deleting destructor. + Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } } else { // Add the return adjustment if necessary. @@ -1951,6 +1950,8 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) { Out << DD->getQualifiedNameAsString(); if (IsComplete) Out << "() [complete]"; + else if (isMicrosoftABI()) + Out << "() [scalar deleting]"; else Out << "() [deleting]"; @@ -2142,8 +2143,8 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) { IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))] = MethodName + " [deleting]"; } else { - IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Complete))] - = MethodName; + IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))] + = MethodName + " [scalar deleting]"; } } else { IndicesMap[VTables.getMethodVTableIndex(MD)] = MethodName; @@ -2275,8 +2276,9 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) { MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); } else { - MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = - getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Complete)); + // Add the scalar deleting destructor. + MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = + getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); } } else { MethodVTableIndices[MD] = getMethodVTableIndex(OverriddenMD); @@ -2302,11 +2304,8 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) { // Add the deleting dtor. MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++; } else { - // Add only the deleting dtor. - // FIXME: The virtual destructors are handled differently in MS ABI, - // we should add such a support later. For now, put the complete - // destructor into the vftable indices. - MethodVTableIndices[GlobalDecl(DD, Dtor_Complete)] = CurrentIndex++; + // Add the scalar deleting dtor. + MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++; } } else { // Add the entry. diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index c319c062ef..80798e71f8 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -54,11 +54,20 @@ protected: return CGF.CXXABIThisValue; } + // FIXME: Every place that calls getVTT{Decl,Value} is something + // that needs to be abstracted properly. ImplicitParamDecl *&getVTTDecl(CodeGenFunction &CGF) { - return CGF.CXXVTTDecl; + return CGF.CXXStructorImplicitParamDecl; } llvm::Value *&getVTTValue(CodeGenFunction &CGF) { - return CGF.CXXVTTValue; + return CGF.CXXStructorImplicitParamValue; + } + + ImplicitParamDecl *&getStructorImplicitParamDecl(CodeGenFunction &CGF) { + return CGF.CXXStructorImplicitParamDecl; + } + llvm::Value *&getStructorImplicitParamValue(CodeGenFunction &CGF) { + return CGF.CXXStructorImplicitParamValue; } /// Build a parameter variable suitable for 'this'. diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index ce32acd0da..4e4acc8f12 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -953,6 +953,32 @@ namespace { } }; + struct CallDtorDeleteConditional : EHScopeStack::Cleanup { + llvm::Value *ShouldDeleteCondition; + public: + CallDtorDeleteConditional(llvm::Value *ShouldDeleteCondition) + : ShouldDeleteCondition(ShouldDeleteCondition) { + assert(ShouldDeleteCondition != NULL); + } + + void Emit(CodeGenFunction &CGF, Flags flags) { + llvm::BasicBlock *callDeleteBB = CGF.createBasicBlock("dtor.call_delete"); + llvm::BasicBlock *continueBB = CGF.createBasicBlock("dtor.continue"); + llvm::Value *ShouldCallDelete + = CGF.Builder.CreateIsNull(ShouldDeleteCondition); + CGF.Builder.CreateCondBr(ShouldCallDelete, continueBB, callDeleteBB); + + CGF.EmitBlock(callDeleteBB); + const CXXDestructorDecl *Dtor = cast(CGF.CurCodeDecl); + const CXXRecordDecl *ClassDecl = Dtor->getParent(); + CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(), + CGF.getContext().getTagDeclType(ClassDecl)); + CGF.Builder.CreateBr(continueBB); + + CGF.EmitBlock(continueBB); + } + }; + class DestroyField : public EHScopeStack::Cleanup { const FieldDecl *field; CodeGenFunction::Destroyer *destroyer; @@ -991,7 +1017,14 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, if (DtorType == Dtor_Deleting) { assert(DD->getOperatorDelete() && "operator delete missing - EmitDtorEpilogue"); - EHStack.pushCleanup(NormalAndEHCleanup); + if (CXXStructorImplicitParamValue) { + // If there is an implicit param to the deleting dtor, it's a boolean + // telling whether we should call delete at the end of the dtor. + EHStack.pushCleanup( + NormalAndEHCleanup, CXXStructorImplicitParamValue); + } else { + EHStack.pushCleanup(NormalAndEHCleanup); + } return; } @@ -1243,7 +1276,8 @@ CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, // FIXME: Provide a source location here. EmitCXXMemberCall(D, SourceLocation(), Callee, ReturnValueSlot(), This, - VTT, ArgBeg, ArgEnd); + VTT, getContext().getPointerType(getContext().VoidPtrTy), + ArgBeg, ArgEnd); } void @@ -1399,7 +1433,8 @@ void CodeGenFunction::EmitCXXDestructorCall(const CXXDestructorDecl *DD, // FIXME: Provide a source location here. EmitCXXMemberCall(DD, SourceLocation(), Callee, ReturnValueSlot(), This, - VTT, 0, 0); + VTT, getContext().getPointerType(getContext().VoidPtrTy), + 0, 0); } namespace { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index d2db71c071..1aaa667413 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -28,7 +28,8 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD, llvm::Value *Callee, ReturnValueSlot ReturnValue, llvm::Value *This, - llvm::Value *VTT, + llvm::Value *ImplicitParam, + QualType ImplicitParamTy, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd) { assert(MD->isInstance() && @@ -46,10 +47,9 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD, // Push the this ptr. Args.add(RValue::get(This), MD->getThisType(getContext())); - // If there is a VTT parameter, emit it. - if (VTT) { - QualType T = getContext().getPointerType(getContext().VoidPtrTy); - Args.add(RValue::get(VTT), T); + // If there is an implicit parameter (e.g. VTT), emit it. + if (ImplicitParam) { + Args.add(RValue::get(ImplicitParam), ImplicitParamTy); } const FunctionProtoType *FPT = MD->getType()->castAs(); @@ -316,7 +316,8 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE, } return EmitCXXMemberCall(MD, CE->getExprLoc(), Callee, ReturnValue, This, - /*VTT=*/0, CE->arg_begin(), CE->arg_end()); + /*ImplicitParam=*/0, QualType(), + CE->arg_begin(), CE->arg_end()); } RValue @@ -388,7 +389,8 @@ CodeGenFunction::EmitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *E, llvm::Value *Callee = EmitCXXOperatorMemberCallee(E, MD, This); return EmitCXXMemberCall(MD, E->getExprLoc(), Callee, ReturnValue, This, - /*VTT=*/0, E->arg_begin() + 1, E->arg_end()); + /*ImplicitParam=*/0, QualType(), + E->arg_begin() + 1, E->arg_end()); } RValue CodeGenFunction::EmitCUDAKernelCallExpr(const CUDAKernelCallExpr *E, @@ -1408,7 +1410,7 @@ static void EmitObjectDelete(CodeGenFunction &CGF, Ptr, Ty); // FIXME: Provide a source location here. CGF.EmitCXXMemberCall(Dtor, SourceLocation(), Callee, ReturnValueSlot(), - Ptr, /*VTT=*/0, 0, 0); + Ptr, /*ImplicitParam=*/0, QualType(), 0, 0); if (UseGlobalDelete) { CGF.PopCleanupBlock(); diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index ec139df3ef..cba3316600 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -43,8 +43,9 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) FirstBlockInfo(0), EHResumeBlock(0), ExceptionSlot(0), EHSelectorSlot(0), DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false), IndirectBranch(0), SwitchInsn(0), CaseRangeBlock(0), UnreachableBlock(0), - CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0), CXXVTTDecl(0), - CXXVTTValue(0), OutermostConditional(0), TerminateLandingPad(0), + CXXABIThisDecl(0), CXXABIThisValue(0), CXXThisValue(0), + CXXStructorImplicitParamDecl(0), CXXStructorImplicitParamValue(0), + OutermostConditional(0), TerminateLandingPad(0), TerminateHandler(0), TrapBB(0) { if (!suppressNewContext) CGM.getCXXABI().getMangleContext().startNewFunction(); diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index d43b58830b..ea5e8733d6 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1179,11 +1179,10 @@ private: llvm::Value *CXXABIThisValue; llvm::Value *CXXThisValue; - /// CXXVTTDecl - When generating code for a base object constructor or - /// base object destructor with virtual bases, this will hold the implicit - /// VTT parameter. - ImplicitParamDecl *CXXVTTDecl; - llvm::Value *CXXVTTValue; + /// CXXStructorImplicitParamDecl - When generating code for a constructor or + /// destructor, this will hold the implicit argument (e.g. VTT). + ImplicitParamDecl *CXXStructorImplicitParamDecl; + llvm::Value *CXXStructorImplicitParamValue; /// OutermostConditional - Points to the outermost active /// conditional control. This is used so that we know if a @@ -1777,9 +1776,19 @@ public: /// LoadCXXVTT - Load the VTT parameter to base constructors/destructors have /// virtual bases. + // FIXME: Every place that calls LoadCXXVTT is something + // that needs to be abstracted properly. llvm::Value *LoadCXXVTT() { - assert(CXXVTTValue && "no VTT value for this function"); - return CXXVTTValue; + assert(CXXStructorImplicitParamValue && "no VTT value for this function"); + return CXXStructorImplicitParamValue; + } + + /// LoadCXXStructorImplicitParam - Load the implicit parameter + /// for a constructor/destructor. + llvm::Value *LoadCXXStructorImplicitParam() { + assert(CXXStructorImplicitParamValue && + "no implicit argument value for this function"); + return CXXStructorImplicitParamValue; } /// GetAddressOfBaseOfCompleteClass - Convert the given pointer to a @@ -2294,7 +2303,8 @@ public: llvm::Value *Callee, ReturnValueSlot ReturnValue, llvm::Value *This, - llvm::Value *VTT, + llvm::Value *ImplicitParam, + QualType ImplicitParamTy, CallExpr::const_arg_iterator ArgBeg, CallExpr::const_arg_iterator ArgEnd); RValue EmitCXXMemberCallExpr(const CXXMemberCallExpr *E, diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 477720bd25..537e4574dd 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -45,10 +45,7 @@ public: void BuildDestructorSignature(const CXXDestructorDecl *Ctor, CXXDtorType Type, CanQualType &ResTy, - SmallVectorImpl &ArgTys) { - // 'this' is already in place - // TODO: 'for base' flag - } + SmallVectorImpl &ArgTys); void BuildInstanceFunctionParams(CodeGenFunction &CGF, QualType &ResTy, @@ -121,6 +118,27 @@ void MicrosoftCXXABI::BuildConstructorSignature(const CXXConstructorDecl *Ctor, ResTy = ArgTys[0]; } +void MicrosoftCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor, + CXXDtorType Type, + CanQualType &ResTy, + SmallVectorImpl &ArgTys) { + // 'this' is already in place + // TODO: 'for base' flag + + if (Type == Dtor_Deleting) { + // The scalar deleting destructor takes an implicit bool parameter. + ArgTys.push_back(CGM.getContext().BoolTy); + } +} + +static bool IsDeletingDtor(GlobalDecl GD) { + const CXXMethodDecl* MD = cast(GD.getDecl()); + if (isa(MD)) { + return GD.getDtorType() == Dtor_Deleting; + } + return false; +} + void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, QualType &ResTy, FunctionArgList &Params) { @@ -128,6 +146,17 @@ void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, if (needThisReturn(CGF.CurGD)) { ResTy = Params[0]->getType(); } + if (IsDeletingDtor(CGF.CurGD)) { + ASTContext &Context = getContext(); + + ImplicitParamDecl *ShouldDelete + = ImplicitParamDecl::Create(Context, 0, + CGF.CurGD.getDecl()->getLocation(), + &Context.Idents.get("should_call_delete"), + Context.BoolTy); + Params.push_back(ShouldDelete); + getStructorImplicitParamDecl(CGF) = ShouldDelete; + } } void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { @@ -135,6 +164,14 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { if (needThisReturn(CGF.CurGD)) { CGF.Builder.CreateStore(getThisValue(CGF), CGF.ReturnValue); } + if (IsDeletingDtor(CGF.CurGD)) { + assert(getStructorImplicitParamDecl(CGF) && + "no implicit parameter for a deleting destructor?"); + getStructorImplicitParamValue(CGF) + = CGF.Builder.CreateLoad( + CGF.GetAddrOfLocalVar(getStructorImplicitParamDecl(CGF)), + "should_call_delete"); + } } bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, diff --git a/test/CodeGenCXX/microsoft-abi-structors.cpp b/test/CodeGenCXX/microsoft-abi-structors.cpp index 12622f1089..16763d5317 100644 --- a/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -1,4 +1,8 @@ -// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 | FileCheck %s +// RUN: %clang_cc1 -emit-llvm %s -o - -cxx-abi microsoft -triple=i386-pc-win32 -fno-rtti > %t 2>&1 +// RUN: FileCheck %s < %t +// Using a different check prefix as the inline destructors might be placed +// anywhere in the output. +// RUN: FileCheck --check-prefix=DTORS %s < %t class A { public: @@ -23,13 +27,60 @@ void no_constructor_destructor_infinite_recursion() { } struct B { - virtual ~B(); + virtual ~B() { +// Complete destructor first: +// DTORS: define {{.*}} x86_thiscallcc void @"\01??1B@@UAE@XZ"(%struct.B* %this) +// +// Then, the scalar deleting destructor (used in the vtable): +// DTORS: define {{.*}} x86_thiscallcc void @"\01??_GB@@UAEPAXI@Z"(%struct.B* %this, i1 zeroext %should_call_delete) +// DTORS: %0 = icmp eq i8 %should_call_delete{{.*}}, 0 +// DTORS-NEXT: br i1 %0, label %dtor.continue, label %dtor.call_delete +// DTORS: dtor.call_delete: +// DTORS-NEXT: %1 = bitcast %struct.B* %this1 to i8* +// DTORS-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %1) nounwind +// DTORS-NEXT: br label %dtor.continue +// DTORS: dtor.continue: +// DTORS-NEXT: ret void + } virtual void foo(); }; +// Emits the vftable in the output. +void B::foo() {} + void check_vftable_offset() { B b; // The vftable pointer should point at the beginning of the vftable. // CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %struct.B* {{.*}} to i8*** // CHECK: store i8** getelementptr inbounds ([2 x i8*]* @"\01??_7B@@6B@", i64 0, i64 0), i8*** [[THIS_PTR]] } + +// FIXME: Enable the following block and add expectations when calls +// to virtual complete dtor are supported. +#if 0 +void call_complete_dtor(B *obj_ptr) { + obj_ptr->~B(); +} +#endif + +void call_deleting_dtor(B *obj_ptr) { +// FIXME: Add CHECKs when calls to virtual deleting dtor are generated properly. + delete obj_ptr; +} + +struct C { + static int foo(); + + C() { + static int ctor_static = foo(); + // CHECK that the static in the ctor gets mangled correctly: + // CHECK: @"\01?ctor_static@?1???0C@@QAE@XZ@4HA" + } + ~C() { + static int dtor_static = foo(); + // CHECK that the static in the dtor gets mangled correctly: + // CHECK: @"\01?dtor_static@?1???1C@@QAE@XZ@4HA" + } +}; + +void use_C() { C c; } diff --git a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp index 176ecf19f1..5d430db54e 100644 --- a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp +++ b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp @@ -37,10 +37,10 @@ void B::f() {} struct C { // CHECK-C: Vtable for 'C' (2 entries) - // CHECK-C-NEXT: 0 | C::~C() + // CHECK-C-NEXT: 0 | C::~C() [scalar deleting] // CHECK-C-NEXT: 1 | void C::f() // CHECK-C: VTable indices for 'C' (2 entries). - // CHECK-C-NEXT: 0 | C::~C() + // CHECK-C-NEXT: 0 | C::~C() [scalar deleting] // CHECK-C-NEXT: 1 | void C::f() // Never used, so doesn't emit a vtable. virtual ~C(); @@ -52,7 +52,7 @@ void C::f() {} struct D { // CHECK-D: Vtable for 'D' (2 entries) // CHECK-D-NEXT: 0 | void D::f() - // CHECK-D-NEXT: 1 | D::~D() + // CHECK-D-NEXT: 1 | D::~D() [scalar deleting] // EMITS-VTABLE: @"\01??_7D@@6B@" = unnamed_addr constant [2 x i8*] virtual void f(); @@ -65,10 +65,10 @@ struct E : A { // CHECK-E-NEXT: 0 | void A::f() // CHECK-E-NEXT: 1 | void A::g() // CHECK-E-NEXT: 2 | void A::h() - // CHECK-E-NEXT: 3 | E::~E() + // CHECK-E-NEXT: 3 | E::~E() [scalar deleting] // CHECK-E-NEXT: 4 | void E::i() // CHECK-E: VTable indices for 'E' (2 entries). - // CHECK-E-NEXT: 3 | E::~E() + // CHECK-E-NEXT: 3 | E::~E() [scalar deleting] // CHECK-E-NEXT: 4 | void E::i() // Never used, so doesn't emit a vtable. @@ -83,10 +83,10 @@ struct F : A { // CHECK-F-NEXT: 1 | void A::g() // CHECK-F-NEXT: 2 | void A::h() // CHECK-F-NEXT: 3 | void F::i() - // CHECK-F-NEXT: 4 | F::~F() + // CHECK-F-NEXT: 4 | F::~F() [scalar deleting] // CHECK-F: VTable indices for 'F' (2 entries). // CHECK-F-NEXT: 3 | void F::i() - // CHECK-F-NEXT: 4 | F::~F() + // CHECK-F-NEXT: 4 | F::~F() [scalar deleting] // EMITS-VTABLE: @"\01??_7F@@6B@" = unnamed_addr constant [5 x i8*] virtual void i(); virtual ~F(); @@ -98,12 +98,12 @@ struct G : E { // CHECK-G-NEXT: 0 | void G::f() // CHECK-G-NEXT: 1 | void A::g() // CHECK-G-NEXT: 2 | void A::h() - // CHECK-G-NEXT: 3 | G::~G() + // CHECK-G-NEXT: 3 | G::~G() [scalar deleting] // CHECK-G-NEXT: 4 | void E::i() // CHECK-G-NEXT: 5 | void G::j() // CHECK-G: VTable indices for 'G' (3 entries). // CHECK-G-NEXT: 0 | void G::f() - // CHECK-G-NEXT: 3 | G::~G() + // CHECK-G-NEXT: 3 | G::~G() [scalar deleting] // CHECK-G-NEXT: 5 | void G::j() // Never used, so doesn't emit a vtable. virtual void f(); // overrides A::f()