Hi Timur, Patch attached -- please review.
The main missing part of this patch is vector deleting destructors, which we don't need ourselves but may be required by MSVC TUs. Thanks, -- Peter
>From 490e935e4070dc28f93934fc847c0264054f1628 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne <[email protected]> Date: Thu, 18 Apr 2013 13:54:53 -0700 Subject: [PATCH] [ms-cxxabi] Get closer to emitting the correct set of destructors in each TU (PR12784). The main missing part of this patch is vector deleting destructors, which we don't need ourselves but may be required by MSVC TUs. --- include/clang/Basic/ABI.h | 7 +- lib/AST/ItaniumMangle.cpp | 2 + lib/AST/MicrosoftMangle.cpp | 19 ++-- lib/AST/VTableBuilder.cpp | 18 ++-- lib/CodeGen/CGCXX.cpp | 52 +++++++---- lib/CodeGen/CGCXXABI.cpp | 8 ++ lib/CodeGen/CGCXXABI.h | 7 ++ lib/CodeGen/CGClass.cpp | 16 ++-- lib/CodeGen/CGVTT.cpp | 18 ---- lib/CodeGen/CGVTables.cpp | 9 +- lib/CodeGen/CGVTables.h | 5 - lib/CodeGen/CodeGenModule.cpp | 34 +++++-- lib/CodeGen/CodeGenModule.h | 10 +- lib/CodeGen/ItaniumCXXABI.cpp | 24 ++++- lib/CodeGen/MicrosoftCXXABI.cpp | 42 ++++++--- lib/Sema/SemaDecl.cpp | 25 ++++- lib/Sema/SemaDeclCXX.cpp | 2 +- .../microsoft-abi-static-initializers.cpp | 8 +- test/CodeGenCXX/microsoft-abi-structors.cpp | 101 ++++++++++++++------- .../microsoft-abi-vtables-single-inheritance.cpp | 18 ++-- 20 files changed, 275 insertions(+), 150 deletions(-) diff --git a/include/clang/Basic/ABI.h b/include/clang/Basic/ABI.h index fecf613..f641b58 100644 --- a/include/clang/Basic/ABI.h +++ b/include/clang/Basic/ABI.h @@ -29,9 +29,10 @@ enum CXXCtorType { /// \brief C++ destructor types. enum CXXDtorType { - Dtor_Deleting, ///< Deleting dtor - Dtor_Complete, ///< Complete object dtor - Dtor_Base ///< Base object dtor + Dtor_Deleting, ///< Deleting dtor + Dtor_Complete, ///< Complete object dtor + Dtor_Base, ///< Base object dtor + Dtor_VectorDeleting ///< Vector deleting dtor }; /// \brief A return adjustment. diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index 0b77933..516ae35 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -3067,6 +3067,8 @@ void CXXNameMangler::mangleCXXDtorType(CXXDtorType T) { case Dtor_Base: Out << "D2"; break; + case Dtor_VectorDeleting: + llvm_unreachable("Itanium ABI does not use vector deleting dtors"); } } diff --git a/lib/AST/MicrosoftMangle.cpp b/lib/AST/MicrosoftMangle.cpp index 9cd6bac..e0bd9a0 100644 --- a/lib/AST/MicrosoftMangle.cpp +++ b/lib/AST/MicrosoftMangle.cpp @@ -489,9 +489,9 @@ MicrosoftCXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, // use the type we were given. mangleCXXDtorType(static_cast<CXXDtorType>(StructorType)); else - // Otherwise, use the complete destructor name. This is relevant if a + // Otherwise, use the base destructor name. This is relevant if a // class with a destructor is declared within a destructor. - mangleCXXDtorType(Dtor_Complete); + mangleCXXDtorType(Dtor_Base); break; case DeclarationName::CXXConversionFunctionName: @@ -551,17 +551,18 @@ void MicrosoftCXXNameMangler::manglePostfix(const DeclContext *DC, void MicrosoftCXXNameMangler::mangleCXXDtorType(CXXDtorType T) { switch (T) { + case Dtor_VectorDeleting: + Out << "?_E"; + return; 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; + case Dtor_Complete: + Out << "?_D"; + return; } llvm_unreachable("Unsupported dtor type?"); } @@ -1141,8 +1142,8 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionType *T, // ::= @ # structors (they have no declared return type) if (IsStructor) { if (isa<CXXDestructorDecl>(D) && D == Structor && - StructorType == Dtor_Deleting) { - // The scalar deleting destructor takes an extra int argument. + (StructorType == Dtor_Deleting || StructorType == Dtor_VectorDeleting)){ + // The deleting destructors take an extra int argument. // However, the FunctionType generated has 0 arguments. // FIXME: This is a temporary hack. // Maybe should fix the FunctionType creation instead? diff --git a/lib/AST/VTableBuilder.cpp b/lib/AST/VTableBuilder.cpp index f80232f..c25804c 100644 --- a/lib/AST/VTableBuilder.cpp +++ b/lib/AST/VTableBuilder.cpp @@ -1304,7 +1304,7 @@ VTableBuilder::AddMethod(const CXXMethodDecl *MD, Components.push_back(VTableComponent::MakeCompleteDtor(DD)); Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } else { - // Add the scalar deleting destructor. + // Add the vector deleting destructor. Components.push_back(VTableComponent::MakeDeletingDtor(DD)); } } else { @@ -1951,7 +1951,7 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) { if (IsComplete) Out << "() [complete]"; else if (isMicrosoftABI()) - Out << "() [scalar deleting]"; + Out << "() [vector deleting]"; else Out << "() [deleting]"; @@ -2143,8 +2143,9 @@ void VTableBuilder::dumpLayout(raw_ostream& Out) { IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))] = MethodName + " [deleting]"; } else { - IndicesMap[VTables.getMethodVTableIndex(GlobalDecl(DD, Dtor_Deleting))] - = MethodName + " [scalar deleting]"; + IndicesMap[VTables.getMethodVTableIndex( + GlobalDecl(DD, Dtor_VectorDeleting))] + = MethodName + " [vector deleting]"; } } else { IndicesMap[VTables.getMethodVTableIndex(MD)] = MethodName; @@ -2276,9 +2277,9 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) { MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); } else { - // Add the scalar deleting destructor. - MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = - getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_Deleting)); + // Add the vector deleting destructor. + MethodVTableIndices[GlobalDecl(DD, Dtor_VectorDeleting)] = + getMethodVTableIndex(GlobalDecl(OverriddenDD, Dtor_VectorDeleting)); } } else { MethodVTableIndices[MD] = getMethodVTableIndex(OverriddenMD); @@ -2305,7 +2306,8 @@ void VTableContext::ComputeMethodVTableIndices(const CXXRecordDecl *RD) { MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++; } else { // Add the scalar deleting dtor. - MethodVTableIndices[GlobalDecl(DD, Dtor_Deleting)] = CurrentIndex++; + MethodVTableIndices[GlobalDecl(DD, Dtor_VectorDeleting)] = + CurrentIndex++; } } else { // Add the entry. diff --git a/lib/CodeGen/CGCXX.cpp b/lib/CodeGen/CGCXX.cpp index 983cb92..09f70a6 100644 --- a/lib/CodeGen/CGCXX.cpp +++ b/lib/CodeGen/CGCXX.cpp @@ -106,8 +106,7 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, // The alias will use the linkage of the referrent. If we can't // support aliases with that linkage, fail. - llvm::GlobalValue::LinkageTypes Linkage - = getFunctionLinkage(cast<FunctionDecl>(AliasDecl.getDecl())); + llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl); switch (Linkage) { // We can definitely emit aliases to definitions with external linkage. @@ -132,11 +131,23 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, } llvm::GlobalValue::LinkageTypes TargetLinkage - = getFunctionLinkage(cast<FunctionDecl>(TargetDecl.getDecl())); + = getFunctionLinkage(TargetDecl); if (llvm::GlobalValue::isWeakForLinker(TargetLinkage)) return true; + EmitDefinitionAsAlias(AliasDecl, TargetDecl); + return false; +} + +/// Emit a definition as a global alias for another definition, unconditionally. +/// Use this function with care as it can produce invalid aliases. Generally +/// this function should be used only where there is an ABI requirement to emit +/// an alias. +void CodeGenModule::EmitDefinitionAsAlias(GlobalDecl AliasDecl, + GlobalDecl TargetDecl) { + llvm::GlobalValue::LinkageTypes Linkage = getFunctionLinkage(AliasDecl); + // Derive the type for the alias. llvm::PointerType *AliasType = getTypes().GetFunctionType(AliasDecl)->getPointerTo(); @@ -169,8 +180,6 @@ bool CodeGenModule::TryEmitDefinitionAsAlias(GlobalDecl AliasDecl, // Finally, set up the alias with its proper name and attributes. SetCommonAttributes(cast<NamedDecl>(AliasDecl.getDecl()), Alias); - - return false; } void CodeGenModule::EmitCXXConstructors(const CXXConstructorDecl *D) { @@ -203,7 +212,7 @@ void CodeGenModule::EmitCXXConstructor(const CXXConstructorDecl *ctor, llvm::Function *fn = cast<llvm::Function>(GetAddrOfCXXConstructor(ctor, ctorType, &fnInfo)); - setFunctionLinkage(ctor, fn); + setFunctionLinkage(GlobalDecl(ctor, ctorType), fn); CodeGenFunction(*this).GenerateCode(GlobalDecl(ctor, ctorType), fn, fnInfo); @@ -230,15 +239,17 @@ CodeGenModule::GetAddrOfCXXConstructor(const CXXConstructorDecl *ctor, } void CodeGenModule::EmitCXXDestructors(const CXXDestructorDecl *D) { - // The destructor in a virtual table is always a 'deleting' - // destructor, which calls the complete destructor and then uses the - // appropriate operator delete. - if (D->isVirtual()) - EmitGlobal(GlobalDecl(D, Dtor_Deleting)); - - // The destructor used for destructing this as a most-derived class; - // call the base destructor and then destructs any virtual bases. - EmitGlobal(GlobalDecl(D, Dtor_Complete)); + if (getTarget().getCXXABI().hasDestructorVariants()) { + // The destructor in a virtual table is always a 'deleting' + // destructor, which calls the complete destructor and then uses the + // appropriate operator delete. + if (D->isVirtual()) + EmitGlobal(GlobalDecl(D, Dtor_Deleting)); + + // The destructor used for destructing this as a most-derived class; + // call the base destructor and then destructs any virtual bases. + EmitGlobal(GlobalDecl(D, Dtor_Complete)); + } // The destructor used for destructing this as a base class; ignores // virtual bases. @@ -262,12 +273,21 @@ void CodeGenModule::EmitCXXDestructor(const CXXDestructorDecl *dtor, if (dtorType == Dtor_Base && !TryEmitBaseDestructorAsAlias(dtor)) return; + // The Microsoft ABI requires that the vector deleting destructor + // be weak aliased to the scalar deleting destructor. + // TODO: emission of the vector deleting destructor (when required). + if (dtorType == Dtor_VectorDeleting) { + EmitDefinitionAsAlias(GlobalDecl(dtor, Dtor_VectorDeleting), + GlobalDecl(dtor, Dtor_Deleting)); + return; + } + const CGFunctionInfo &fnInfo = getTypes().arrangeCXXDestructor(dtor, dtorType); llvm::Function *fn = cast<llvm::Function>(GetAddrOfCXXDestructor(dtor, dtorType, &fnInfo)); - setFunctionLinkage(dtor, fn); + setFunctionLinkage(GlobalDecl(dtor, dtorType), fn); CodeGenFunction(*this).GenerateCode(GlobalDecl(dtor, dtorType), fn, fnInfo); diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 68fecb2..1d07aa5 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -270,3 +270,11 @@ LValue CGCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, ErrorUnsupportedABI(CGF, "odr-use of thread_local global"); return LValue(); } + +bool CGCXXABI::NeedsBodylessEmission(GlobalDecl GD) { + return false; +} + +bool CGCXXABI::NeedsVTTParameter(GlobalDecl GD) { + return false; +} diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index 1e4da63..ca7092d 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -312,6 +312,13 @@ public: QualType ElementType, llvm::Value *&NumElements, llvm::Value *&AllocPtr, CharUnits &CookieSize); + /// Returns true if the ABI requires that we emit a definition for this + /// function, given that we don't have a definition for it in the TU. + virtual bool NeedsBodylessEmission(GlobalDecl GD); + + /// Return whether the given global decl needs a VTT parameter. + virtual bool NeedsVTTParameter(GlobalDecl GD); + protected: /// Returns the extra size required in order to store the array /// cookie for the given type. Assumes that an array cookie is diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 2b9a55b..86bf1c9 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -285,7 +285,7 @@ CodeGenFunction::GetAddressOfDerivedClass(llvm::Value *Value, llvm::Value *CodeGenFunction::GetVTTParameter(GlobalDecl GD, bool ForVirtualBase, bool Delegating) { - if (!CodeGenVTables::needsVTTParameter(GD)) { + if (!CGM.getCXXABI().NeedsVTTParameter(GD)) { // This constructor/destructor does not need a VTT parameter. return 0; } @@ -303,7 +303,7 @@ llvm::Value *CodeGenFunction::GetVTTParameter(GlobalDecl GD, } else if (RD == Base) { // If the record matches the base, this is the complete ctor/dtor // variant calling the base variant in a class with virtual bases. - assert(!CodeGenVTables::needsVTTParameter(CurGD) && + assert(!CGM.getCXXABI().NeedsVTTParameter(CurGD) && "doing no-op VTT offset in base dtor/ctor?"); assert(!ForVirtualBase && "Can't have same class as virtual base!"); SubVTTIndex = 0; @@ -318,7 +318,7 @@ llvm::Value *CodeGenFunction::GetVTTParameter(GlobalDecl GD, assert(SubVTTIndex != 0 && "Sub-VTT index must be greater than zero!"); } - if (CodeGenVTables::needsVTTParameter(CurGD)) { + if (CGM.getCXXABI().NeedsVTTParameter(CurGD)) { // A VTT parameter was passed to the constructor, use it. VTT = LoadCXXVTT(); VTT = Builder.CreateConstInBoundsGEP1_64(VTT, SubVTTIndex); @@ -1273,13 +1273,13 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) { // we'd introduce *two* handler blocks. switch (DtorType) { case Dtor_Deleting: llvm_unreachable("already handled deleting case"); + case Dtor_VectorDeleting: llvm_unreachable("TODO"); case Dtor_Complete: // Enter the cleanup scopes for virtual bases. EnterDtorCleanups(Dtor, Dtor_Complete); - if (!isTryBody && - CGM.getTarget().getCXXABI().hasDestructorVariants()) { + if (!isTryBody) { EmitCXXDestructorCall(Dtor, Dtor_Base, /*ForVirtualBase=*/false, /*Delegating=*/false, LoadCXXThis()); break; @@ -1747,7 +1747,7 @@ CodeGenFunction::EmitDelegateCXXConstructorCall(const CXXConstructorDecl *Ctor, QualType VoidPP = getContext().getPointerType(getContext().VoidPtrTy); DelegateArgs.add(RValue::get(VTT), VoidPP); - if (CodeGenVTables::needsVTTParameter(CurGD)) { + if (CGM.getCXXABI().NeedsVTTParameter(CurGD)) { assert(I != E && "cannot skip vtt parameter, already done with args"); assert((*I)->getType() == VoidPP && "skipping parameter not of vtt type"); ++I; @@ -1902,7 +1902,7 @@ CodeGenFunction::InitializeVTablePointer(BaseSubobject Base, llvm::Value *VTableAddressPoint; // Check if we need to use a vtable from the VTT. - if (CodeGenVTables::needsVTTParameter(CurGD) && + if (CGM.getCXXABI().NeedsVTTParameter(CurGD) && (RD->getNumVBases() || NearestVBase)) { // Get the secondary vpointer index. uint64_t VirtualPointerIndex = @@ -1926,7 +1926,7 @@ CodeGenFunction::InitializeVTablePointer(BaseSubobject Base, llvm::Value *VirtualOffset = 0; CharUnits NonVirtualOffset = CharUnits::Zero(); - if (CodeGenVTables::needsVTTParameter(CurGD) && NearestVBase) { + if (CGM.getCXXABI().NeedsVTTParameter(CurGD) && NearestVBase) { // We need to use the virtual base offset offset because the virtual base // might have a different offset in the most derived class. VirtualOffset = GetVirtualBaseClassOffset(LoadCXXThis(), VTableClass, diff --git a/lib/CodeGen/CGVTT.cpp b/lib/CodeGen/CGVTT.cpp index 98be872..a02292e 100644 --- a/lib/CodeGen/CGVTT.cpp +++ b/lib/CodeGen/CGVTT.cpp @@ -120,24 +120,6 @@ llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTT(const CXXRecordDecl *RD) { return GV; } -bool CodeGenVTables::needsVTTParameter(GlobalDecl GD) { - const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); - - // We don't have any virtual bases, just return early. - if (!MD->getParent()->getNumVBases()) - return false; - - // Check if we have a base constructor. - if (isa<CXXConstructorDecl>(MD) && GD.getCtorType() == Ctor_Base) - return true; - - // Check if we have a base destructor. - if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base) - return true; - - return false; -} - uint64_t CodeGenVTables::getSubVTTIndex(const CXXRecordDecl *RD, BaseSubobject Base) { BaseSubobjectPairTy ClassSubobjectPair(RD, Base); diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 069cd5f..a071c5b 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -379,7 +379,7 @@ void CodeGenFunction::GenerateThunk(llvm::Function *Fn, FinishFunction(); // Set the right linkage. - CGM.setFunctionLinkage(MD, Fn); + CGM.setFunctionLinkage(GD, Fn); // Set the right visibility. setThunkVisibility(CGM, MD, Thunk, Fn); @@ -437,7 +437,7 @@ void CodeGenVTables::EmitThunk(GlobalDecl GD, const ThunkInfo &Thunk, "Function should have available_externally linkage!"); // Change the linkage. - CGM.setFunctionLinkage(cast<CXXMethodDecl>(GD.getDecl()), ThunkFn); + CGM.setFunctionLinkage(GD, ThunkFn); return; } @@ -554,7 +554,10 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD, GD = GlobalDecl(Component.getDestructorDecl(), Dtor_Complete); break; case VTableComponent::CK_DeletingDtorPointer: - GD = GlobalDecl(Component.getDestructorDecl(), Dtor_Deleting); + GD = GlobalDecl(Component.getDestructorDecl(), + CGM.getTarget().getCXXABI().isMicrosoft() + ? Dtor_VectorDeleting + : Dtor_Deleting); break; } diff --git a/lib/CodeGen/CGVTables.h b/lib/CodeGen/CGVTables.h index bd3bdb1..fe51d50 100644 --- a/lib/CodeGen/CGVTables.h +++ b/lib/CodeGen/CGVTables.h @@ -77,11 +77,6 @@ public: VTableContext &getVTableContext() { return VTContext; } - /// needsVTTParameter - Return whether the given global decl needs a VTT - /// parameter, which it does if it's a base constructor or destructor with - /// virtual bases. - static bool needsVTTParameter(GlobalDecl GD); - /// getSubVTTIndex - Return the index of the sub-VTT for the base class of the /// given record decl. uint64_t getSubVTTIndex(const CXXRecordDecl *RD, BaseSubobject Base); diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 5a10602..1a4b720 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -508,7 +508,8 @@ void CodeGenModule::EmitCtorList(const CtorList &Fns, const char *GlobalName) { } llvm::GlobalValue::LinkageTypes -CodeGenModule::getFunctionLinkage(const FunctionDecl *D) { +CodeGenModule::getFunctionLinkage(GlobalDecl GD) { + const FunctionDecl *D = cast<FunctionDecl>(GD.getDecl()); GVALinkage Linkage = getContext().GetGVALinkageForFunction(D); if (Linkage == GVA_Internal) @@ -525,6 +526,19 @@ CodeGenModule::getFunctionLinkage(const FunctionDecl *D) { if (Linkage == GVA_C99Inline) return llvm::Function::AvailableExternallyLinkage; + // In the Microsoft ABI, most destructor types are not emitted with the + // definition, and must be emitted on demand. Give these linkonce_odr + // linkage, with the exception of vector deleting destructors, which get + // weak linkage because they may not necessarily be referenced in the + // translation unit which requires them to be emitted, and they may be + // overridden by other translation units. + if (getTarget().getCXXABI().isMicrosoft() && isa<CXXDestructorDecl>(D)) { + if (GD.getDtorType() == Dtor_VectorDeleting) + return llvm::Function::WeakAnyLinkage; + else if (GD.getDtorType() != Dtor_Base) + return llvm::Function::LinkOnceODRLinkage; + } + // Note that Apple's kernel linker doesn't support symbol // coalescing, so we need to avoid linkonce and weak linkages there. // Normally, this means we just map to internal, but for explicit @@ -1203,18 +1217,19 @@ CodeGenModule::isTriviallyRecursive(const FunctionDecl *FD) { } bool -CodeGenModule::shouldEmitFunction(const FunctionDecl *F) { - if (getFunctionLinkage(F) != llvm::Function::AvailableExternallyLinkage) +CodeGenModule::shouldEmitFunction(GlobalDecl GD) { + if (getFunctionLinkage(GD) != llvm::Function::AvailableExternallyLinkage) return true; if (CodeGenOpts.OptimizationLevel == 0 && - !F->hasAttr<AlwaysInlineAttr>() && !F->hasAttr<ForceInlineAttr>()) + !GD.getDecl()->hasAttr<AlwaysInlineAttr>() && + !GD.getDecl()->hasAttr<ForceInlineAttr>()) return false; // PR9614. Avoid cases where the source code is lying to us. An available // externally function should have an equivalent function somewhere else, // but a function that calls itself is clearly not equivalent to the real // implementation. // This happens in glibc's btowc and in some configure checks. - return !isTriviallyRecursive(F); + return !isTriviallyRecursive(cast<FunctionDecl>(GD.getDecl())); } void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD) { @@ -1224,10 +1239,10 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD) { Context.getSourceManager(), "Generating code for declaration"); - if (const FunctionDecl *Function = dyn_cast<FunctionDecl>(D)) { + if (isa<FunctionDecl>(D)) { // At -O0, don't generate IR for functions with available_externally // linkage. - if (!shouldEmitFunction(Function)) + if (!shouldEmitFunction(GD)) return; if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) { @@ -1341,7 +1356,8 @@ CodeGenModule::GetOrCreateLLVMFunction(StringRef MangledName, assert(FD->isUsed() && "Sema didn't mark implicit function as used!"); DeferredDeclsToEmit.push_back(D.getWithDecl(FD)); break; - } else if (FD->doesThisDeclarationHaveABody()) { + } else if (FD->doesThisDeclarationHaveABody() || + getCXXABI().NeedsBodylessEmission(D)) { DeferredDeclsToEmit.push_back(D.getWithDecl(FD)); break; } @@ -2120,7 +2136,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD) { // want to propagate this information down (e.g. to local static // declarations). llvm::Function *Fn = cast<llvm::Function>(Entry); - setFunctionLinkage(D, Fn); + setFunctionLinkage(GD, Fn); // FIXME: this is redundant with part of SetFunctionDefinitionAttributes setGlobalVisibility(Fn, D); diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 0f4fe8a..a0b370a 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -382,7 +382,7 @@ class CodeGenModule : public CodeGenTypeCache { void createCUDARuntime(); bool isTriviallyRecursive(const FunctionDecl *F); - bool shouldEmitFunction(const FunctionDecl *F); + bool shouldEmitFunction(GlobalDecl GD); /// @name Cache for Blocks Runtime Globals /// @{ @@ -907,11 +907,10 @@ public: void EmitVTable(CXXRecordDecl *Class, bool DefinitionRequired); - llvm::GlobalVariable::LinkageTypes - getFunctionLinkage(const FunctionDecl *FD); + llvm::GlobalVariable::LinkageTypes getFunctionLinkage(GlobalDecl GD); - void setFunctionLinkage(const FunctionDecl *FD, llvm::GlobalValue *V) { - V->setLinkage(getFunctionLinkage(FD)); + void setFunctionLinkage(GlobalDecl GD, llvm::GlobalValue *V) { + V->setLinkage(getFunctionLinkage(GD)); } /// getVTableLinkage - Return the appropriate linkage for the vtable, VTT, @@ -1014,6 +1013,7 @@ private: bool TryEmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target); bool TryEmitBaseDestructorAsAlias(const CXXDestructorDecl *D); + void EmitDefinitionAsAlias(GlobalDecl Alias, GlobalDecl Target); void EmitNamespace(const NamespaceDecl *D); void EmitLinkageSpec(const LinkageSpecDecl *D); diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 2714c9e..2d1a322 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -154,6 +154,8 @@ public: llvm::Function *InitFunc); LValue EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, const DeclRefExpr *DRE); + + bool NeedsVTTParameter(GlobalDecl GD); }; class ARMCXXABI : public ItaniumCXXABI { @@ -796,7 +798,7 @@ void ItaniumCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, assert(MD->isInstance()); // Check if we need a VTT parameter as well. - if (CodeGenVTables::needsVTTParameter(CGF.CurGD)) { + if (NeedsVTTParameter(CGF.CurGD)) { ASTContext &Context = getContext(); // FIXME: avoid the fake decl @@ -1392,3 +1394,23 @@ LValue ItaniumCXXABI::EmitThreadLocalDeclRefExpr(CodeGenFunction &CGF, // FIXME: need setObjCGCLValueClass? return LV; } + +/// Return whether the given global decl needs a VTT parameter, which it does +/// if it's a base constructor or destructor with virtual bases. +bool ItaniumCXXABI::NeedsVTTParameter(GlobalDecl GD) { + const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); + + // We don't have any virtual bases, just return early. + if (!MD->getParent()->getNumVBases()) + return false; + + // Check if we have a base constructor. + if (isa<CXXConstructorDecl>(MD) && GD.getCtorType() == Ctor_Base) + return true; + + // Check if we have a base destructor. + if (isa<CXXDestructorDecl>(MD) && GD.getDtorType() == Dtor_Base) + return true; + + return false; +} diff --git a/lib/CodeGen/MicrosoftCXXABI.cpp b/lib/CodeGen/MicrosoftCXXABI.cpp index 92bc538..130f8e4 100644 --- a/lib/CodeGen/MicrosoftCXXABI.cpp +++ b/lib/CodeGen/MicrosoftCXXABI.cpp @@ -164,6 +164,8 @@ public: llvm::Value *MemPtr, const MemberPointerType *MPT); + virtual bool NeedsBodylessEmission(GlobalDecl GD); + }; } @@ -224,16 +226,17 @@ void MicrosoftCXXABI::BuildDestructorSignature(const CXXDestructorDecl *Dtor, // '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); + if (Type == Dtor_Deleting || Type == Dtor_VectorDeleting) { + // The deleting destructors take an implicit int parameter. + ArgTys.push_back(CGM.getContext().IntTy); } } static bool IsDeletingDtor(GlobalDecl GD) { const CXXMethodDecl* MD = cast<CXXMethodDecl>(GD.getDecl()); if (isa<CXXDestructorDecl>(MD)) { - return GD.getDtorType() == Dtor_Deleting; + return GD.getDtorType() == Dtor_Deleting || + GD.getDtorType() == Dtor_VectorDeleting; } return false; } @@ -257,13 +260,13 @@ void MicrosoftCXXABI::BuildInstanceFunctionParams(CodeGenFunction &CGF, Params.push_back(IsMostDerived); getStructorImplicitParamDecl(CGF) = IsMostDerived; } else if (IsDeletingDtor(CGF.CurGD)) { - ImplicitParamDecl *ShouldDelete + ImplicitParamDecl *Flags = ImplicitParamDecl::Create(Context, 0, CGF.CurGD.getDecl()->getLocation(), - &Context.Idents.get("should_call_delete"), - Context.BoolTy); - Params.push_back(ShouldDelete); - getStructorImplicitParamDecl(CGF) = ShouldDelete; + &Context.Idents.get("flags"), + Context.IntTy); + Params.push_back(Flags); + getStructorImplicitParamDecl(CGF) = Flags; } } @@ -289,7 +292,7 @@ void MicrosoftCXXABI::EmitInstanceFunctionProlog(CodeGenFunction &CGF) { getStructorImplicitParamValue(CGF) = CGF.Builder.CreateLoad( CGF.GetAddrOfLocalVar(getStructorImplicitParamDecl(CGF)), - "should_call_delete"); + "flags"); } } @@ -328,17 +331,18 @@ RValue MicrosoftCXXABI::EmitVirtualDestructorCall(CodeGenFunction &CGF, // We have only one destructor in the vftable but can get both behaviors // by passing an implicit bool parameter. const CGFunctionInfo *FInfo - = &CGM.getTypes().arrangeCXXDestructor(Dtor, Dtor_Deleting); + = &CGM.getTypes().arrangeCXXDestructor(Dtor, Dtor_VectorDeleting); llvm::Type *Ty = CGF.CGM.getTypes().GetFunctionType(*FInfo); - llvm::Value *Callee = CGF.BuildVirtualCall(Dtor, Dtor_Deleting, This, Ty); + llvm::Value *Callee + = CGF.BuildVirtualCall(Dtor, Dtor_VectorDeleting, This, Ty); ASTContext &Context = CGF.getContext(); llvm::Value *ImplicitParam - = llvm::ConstantInt::get(llvm::IntegerType::getInt1Ty(CGF.getLLVMContext()), - DtorType == Dtor_Deleting); + = llvm::ConstantInt::get(llvm::IntegerType::getInt32Ty(CGF.getLLVMContext()), + DtorType == Dtor_Deleting); return CGF.EmitCXXMemberCall(Dtor, CallLoc, Callee, ReturnValue, This, - ImplicitParam, Context.BoolTy, 0, 0); + ImplicitParam, Context.IntTy, 0, 0); } bool MicrosoftCXXABI::requiresArrayCookie(const CXXDeleteExpr *expr, @@ -711,6 +715,14 @@ MicrosoftCXXABI::EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, return Builder.CreateBitCast(FunctionPointer, FTy->getPointerTo()); } +bool MicrosoftCXXABI::NeedsBodylessEmission(GlobalDecl GD) { + // In the Microsoft ABI, all destructor types except the base destructor are + // not emitted with the definition, and must be emitted on demand. + if (isa<CXXDestructorDecl>(GD.getDecl())) + return GD.getDtorType() != Dtor_Base; + return false; +} + CGCXXABI *clang::CodeGen::CreateMicrosoftCXXABI(CodeGenModule &CGM) { return new MicrosoftCXXABI(CGM); } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 380b53f..1c974cb 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -5742,6 +5742,15 @@ static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D, SemaRef.AdjustDestructorExceptionSpec(Record, NewDD); } + // The Microsoft ABI requires that we perform the destructor body + // checks (i.e. operator delete() lookup) at every declaration, as + // any translation unit may need to emit a deleting destructor. + if (SemaRef.Context.getTargetInfo().getCXXABI().isMicrosoft() && + !Record->isDependentType() && Record->getDefinition() && + !Record->isBeingDefined()) { + SemaRef.CheckDestructor(NewDD); + } + IsVirtualOkay = true; return NewDD; @@ -11085,10 +11094,18 @@ void Sema::ActOnFields(Scope* S, I.setAccess((*I)->getAccess()); if (!CXXRecord->isDependentType()) { - // Adjust user-defined destructor exception spec. - if (getLangOpts().CPlusPlus11 && - CXXRecord->hasUserDeclaredDestructor()) - AdjustDestructorExceptionSpec(CXXRecord,CXXRecord->getDestructor()); + if (CXXRecord->hasUserDeclaredDestructor()) { + // Adjust user-defined destructor exception spec. + if (getLangOpts().CPlusPlus11) + AdjustDestructorExceptionSpec(CXXRecord, + CXXRecord->getDestructor()); + + // The Microsoft ABI requires that we perform the destructor body + // checks (i.e. operator delete() lookup) at every declaration, as + // any translation unit may need to emit a deleting destructor. + if (Context.getTargetInfo().getCXXABI().isMicrosoft()) + CheckDestructor(CXXRecord->getDestructor()); + } // Add any implicitly-declared members to this class. AddImplicitlyDeclaredMembersToClass(CXXRecord); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index d18b9d3..4f1f327 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -5895,7 +5895,7 @@ void Sema::CheckConstructor(CXXConstructorDecl *Constructor) { bool Sema::CheckDestructor(CXXDestructorDecl *Destructor) { CXXRecordDecl *RD = Destructor->getParent(); - if (Destructor->isVirtual()) { + if (!Destructor->getOperatorDelete() && Destructor->isVirtual()) { SourceLocation Loc; if (!Destructor->isImplicit()) diff --git a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp index 35e343b..23c663d 100644 --- a/test/CodeGenCXX/microsoft-abi-static-initializers.cpp +++ b/test/CodeGenCXX/microsoft-abi-static-initializers.cpp @@ -11,7 +11,7 @@ struct S { // CHECK: ret void // CHECK: define internal void @"__dtor_\01?s@@3US@@A"() [[NUW]] { -// CHECK: call x86_thiscallcc void @"\01??1S@@QAE@XZ" +// CHECK: call x86_thiscallcc void @"\01??_DS@@QAE@XZ" // CHECK: ret void // Force WeakODRLinkage by using templates @@ -40,12 +40,12 @@ void force_usage() { // CHECK: define linkonce_odr x86_thiscallcc %class.A* @"\01??0A@@QAE@XZ" -// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1A@@QAE@XZ" - // CHECK: define internal void [[FOO_DTOR]] -// CHECK: call x86_thiscallcc void @"\01??1A@@QAE@XZ"{{.*}}foo +// CHECK: call x86_thiscallcc void @"\01??_DA@@QAE@XZ"{{.*}}foo // CHECK: ret void +// CHECK: define linkonce_odr x86_thiscallcc void @"\01??1A@@QAE@XZ" + // CHECK: define internal void @_GLOBAL__I_a() [[NUW]] { // CHECK: call void [[INIT_s]] // CHECK: call void [[INIT_foo]] diff --git a/test/CodeGenCXX/microsoft-abi-structors.cpp b/test/CodeGenCXX/microsoft-abi-structors.cpp index 864540d..4e129f1 100644 --- a/test/CodeGenCXX/microsoft-abi-structors.cpp +++ b/test/CodeGenCXX/microsoft-abi-structors.cpp @@ -1,8 +1,13 @@ // 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 +// Using a different check prefix as the inline con/destructors might be placed // anywhere in the output. -// RUN: FileCheck --check-prefix=DTORS %s < %t +// RUN: FileCheck --check-prefix=CTOR1 %s < %t +// RUN: FileCheck --check-prefix=DTOR1 %s < %t +// RUN: FileCheck --check-prefix=DTOR2 %s < %t +// RUN: FileCheck --check-prefix=DTOR3 %s < %t +// RUN: FileCheck --check-prefix=DTOR4 %s < %t +// RUN: FileCheck --check-prefix=DTOR5 %s < %t namespace basic { @@ -23,13 +28,14 @@ void no_constructor_destructor_infinite_recursion() { // CHECK-NEXT: } // Make sure that the destructor doesn't call itself: -// CHECK: define {{.*}} @"\01??1A@basic@@QAE@XZ" -// CHECK-NOT: call void @"\01??1A@basic@@QAE@XZ" -// CHECK: ret +// DTOR1: define {{.*}} @"\01??1A@basic@@QAE@XZ" +// DTOR1-NOT: call void @"\01??1A@basic@@QAE@XZ" +// DTOR1: ret } struct B { B(); + ~B(); }; // Tests that we can define constructors outside the class (PR12784). @@ -38,29 +44,39 @@ B::B() { // CHECK: ret } +// Tests that we can define destructors outside the class. +B::~B() { + // CHECK: define x86_thiscallcc void @"\01??1B@basic@@QAE@XZ"(%"struct.basic::B"* %this) + // CHECK: ret +} + +// We shouldn't emit the other destructor forms as they aren't needed by the TU. +// CHECK-NOT: @"\01??_DB@basic@@UAE@XZ" +// CHECK-NOT: @"\01??_GB@basic@@UAEPAXI@Z" +// CHECK-NOT: @"\01??_EB@basic@@UAEPAXI@Z" + struct C { virtual ~C() { // Complete destructor first: -// DTORS: define {{.*}} x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %this) +// DTOR2: define {{.*}} x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %this) // Then, the scalar deleting destructor (used in the vtable): // FIXME: add a test that verifies that the out-of-line scalar deleting // destructor is linkonce_odr too. -// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i1 zeroext %should_call_delete) -// DTORS: %[[FROMBOOL:[0-9a-z]+]] = zext i1 %should_call_delete to i8 -// DTORS-NEXT: store i8 %[[FROMBOOL]], i8* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 1 -// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i8* %[[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 i8 %[[SHOULD_DELETE_VALUE]], 0 -// DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]] +// DTOR3: define linkonce_odr x86_thiscallcc void @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %flags) +// DTOR3: store i32 %flags, i32* %[[FLAGS_VAR:[0-9a-z._]+]], align 4 +// DTOR3: %[[FLAGS_VALUE:[0-9a-z._]+]] = load i32* %[[FLAGS_VAR]] +// DTOR3: call x86_thiscallcc void @"\01??_DC@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]]) +// DTOR3-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[FLAGS_VALUE]], 0 +// DTOR3-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]] // -// DTORS: [[CALL_DELETE_LABEL]] -// DTORS-NEXT: %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8* -// DTORS-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[THIS_AS_VOID]]) [[NUW:#[0-9]+]] -// DTORS-NEXT: br label %[[CONTINUE_LABEL]] +// DTOR3: [[CALL_DELETE_LABEL]] +// DTOR3-NEXT: %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8* +// DTOR3-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[THIS_AS_VOID]]) [[NUW:#[0-9]+]] +// DTOR3-NEXT: br label %[[CONTINUE_LABEL]] // -// DTORS: [[CONTINUE_LABEL]] -// DTORS-NEXT: ret void +// DTOR3: [[CONTINUE_LABEL]] +// DTOR3-NEXT: ret void } virtual void foo(); }; @@ -79,11 +95,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"*, i1)*** -// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i1)*** %[[PVTABLE]] -// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i1)** %[[VTABLE]], i64 0 -// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i1)** %[[PVDTOR]] -// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i1 zeroext false) +// 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: ret void } @@ -94,11 +110,11 @@ 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"*, i1)*** -// CHECK-NEXT: %[[VTABLE:.*]] = load void (%"struct.basic::C"*, i1)*** %[[PVTABLE]] -// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds void (%"struct.basic::C"*, i1)** %[[VTABLE]], i64 0 -// CHECK-NEXT: %[[VDTOR:.*]] = load void (%"struct.basic::C"*, i1)** %[[PVDTOR]] -// CHECK-NEXT: call x86_thiscallcc void %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i1 zeroext true) +// 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: ret void } @@ -108,18 +124,18 @@ struct D { D() { static int ctor_static = foo(); // CHECK that the static in the ctor gets mangled correctly: - // CHECK: @"\01?ctor_static@?1???0D@basic@@QAE@XZ@4HA" + // CTOR1: @"\01?ctor_static@?1???0D@basic@@QAE@XZ@4HA" } ~D() { static int dtor_static = foo(); // CHECK that the static in the dtor gets mangled correctly: - // CHECK: @"\01?dtor_static@?1???1D@basic@@QAE@XZ@4HA" + // DTOR4: @"\01?dtor_static@?1???1D@basic@@QAE@XZ@4HA" } }; void use_D() { D c; } -// DTORS: attributes [[NUW]] = { nounwind{{.*}} } +// DTOR3: attributes [[NUW]] = { nounwind{{.*}} } } // end namespace basic @@ -213,3 +229,24 @@ E::E() { } } // end namespace constructors + +namespace bodyless_dtors { + +struct A { + A(); + virtual void f(); // TODO: remove this function once we disable the key + // function logic for the MS ABI. + virtual ~A(); +}; + +A::A() {} +void A::f() {} + +// The emission of the ctor requires emission of the vtable and therefore +// of the other dtor forms, despite not being defined in this TU. + +// DTOR5: @"\01??_EA@bodyless_dtors@@UAEPAXI@Z" = alias weak void (%"struct.bodyless_dtors::A"*, i32)* @"\01??_GA@bodyless_dtors@@UAEPAXI@Z" +// DTOR5: define linkonce_odr x86_thiscallcc void @"\01??_GA@bodyless_dtors@@UAEPAXI@Z"(%"struct.bodyless_dtors::A"* %this, i32 %flags) +// DTORS: define linkonce_odr x86_thiscallcc void @"\01??_DA@bodyless_dtors@@UAE@XZ"(%"struct.bodyless_dtors::A"* %this) + +} diff --git a/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp b/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp index 5d430db..9785918 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() [scalar deleting] + // CHECK-C-NEXT: 0 | C::~C() [vector deleting] // CHECK-C-NEXT: 1 | void C::f() // CHECK-C: VTable indices for 'C' (2 entries). - // CHECK-C-NEXT: 0 | C::~C() [scalar deleting] + // CHECK-C-NEXT: 0 | C::~C() [vector 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() [scalar deleting] + // CHECK-D-NEXT: 1 | D::~D() [vector 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() [scalar deleting] + // CHECK-E-NEXT: 3 | E::~E() [vector deleting] // CHECK-E-NEXT: 4 | void E::i() // CHECK-E: VTable indices for 'E' (2 entries). - // CHECK-E-NEXT: 3 | E::~E() [scalar deleting] + // CHECK-E-NEXT: 3 | E::~E() [vector 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() [scalar deleting] + // CHECK-F-NEXT: 4 | F::~F() [vector deleting] // CHECK-F: VTable indices for 'F' (2 entries). // CHECK-F-NEXT: 3 | void F::i() - // CHECK-F-NEXT: 4 | F::~F() [scalar deleting] + // CHECK-F-NEXT: 4 | F::~F() [vector 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() [scalar deleting] + // CHECK-G-NEXT: 3 | G::~G() [vector 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() [scalar deleting] + // CHECK-G-NEXT: 3 | G::~G() [vector deleting] // CHECK-G-NEXT: 5 | void G::j() // Never used, so doesn't emit a vtable. virtual void f(); // overrides A::f() -- 1.8.2.1
_______________________________________________ cfe-commits mailing list [email protected] http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
