Index: include/clang/AST/VTableBuilder.h =================================================================== --- include/clang/AST/VTableBuilder.h +++ include/clang/AST/VTableBuilder.h @@ -123,30 +123,50 @@ const CXXRecordDecl *getRTTIDecl() const { assert(getKind() == CK_RTTI && "Invalid component kind!"); - return reinterpret_cast(getPointer()); } const CXXMethodDecl *getFunctionDecl() const { - assert(getKind() == CK_FunctionPointer); - + assert(isFunctionPointerKind() && "Invalid component kind!"); + if (isDestructorKind()) + return getDestructorDecl(); return reinterpret_cast(getPointer()); } const CXXDestructorDecl *getDestructorDecl() const { - assert((getKind() == CK_CompleteDtorPointer || - getKind() == CK_DeletingDtorPointer) && "Invalid component kind!"); - + assert(isDestructorKind() && "Invalid component kind!"); return reinterpret_cast(getPointer()); } const CXXMethodDecl *getUnusedFunctionDecl() const { - assert(getKind() == CK_UnusedFunctionPointer); - + assert(getKind() == CK_UnusedFunctionPointer && "Invalid component kind!"); return reinterpret_cast(getPointer()); } + bool isDestructorKind() const { return isDestructorKind(getKind()); } + + bool isUsedFunctionPointerKind() const { + return isUsedFunctionPointerKind(getKind()); + } + + bool isFunctionPointerKind() const { + return isFunctionPointerKind(getKind()); + } + private: + static bool isFunctionPointerKind(Kind ComponentKind) { + return isUsedFunctionPointerKind(ComponentKind) || + ComponentKind == CK_UnusedFunctionPointer; + } + static bool isUsedFunctionPointerKind(Kind ComponentKind) { + return ComponentKind == CK_FunctionPointer || + isDestructorKind(ComponentKind); + } + static bool isDestructorKind(Kind ComponentKind) { + return ComponentKind == CK_CompleteDtorPointer || + ComponentKind == CK_DeletingDtorPointer; + } + VTableComponent(Kind ComponentKind, CharUnits Offset) { assert((ComponentKind == CK_VCallOffset || ComponentKind == CK_VBaseOffset || @@ -158,12 +178,8 @@ } VTableComponent(Kind ComponentKind, uintptr_t Ptr) { - assert((ComponentKind == CK_RTTI || - ComponentKind == CK_FunctionPointer || - ComponentKind == CK_CompleteDtorPointer || - ComponentKind == CK_DeletingDtorPointer || - ComponentKind == CK_UnusedFunctionPointer) && - "Invalid component kind!"); + assert((ComponentKind == CK_RTTI || isFunctionPointerKind(ComponentKind)) && + "Invalid component kind!"); assert((Ptr & 7) == 0 && "Pointer not sufficiently aligned!"); @@ -178,11 +194,7 @@ } uintptr_t getPointer() const { - assert((getKind() == CK_RTTI || - getKind() == CK_FunctionPointer || - getKind() == CK_CompleteDtorPointer || - getKind() == CK_DeletingDtorPointer || - getKind() == CK_UnusedFunctionPointer) && + assert((getKind() == CK_RTTI || isFunctionPointerKind()) && "Invalid component kind!"); return static_cast(Value & ~7ULL); Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -320,17 +320,15 @@ void emitCXXStructor(const CXXMethodDecl *MD, StructorType Type) override; private: - /// Checks if function has any virtual inline function. - bool hasAnyVirtualInlineFunction(const CXXRecordDecl *RD) const { + bool hasAnyUsedVirtualInlineFunction(const CXXRecordDecl *RD) const { const auto &VtableLayout = CGM.getItaniumVTableContext().getVTableLayout(RD); for (const auto &VtableComponent : VtableLayout.vtable_components()) { - if (VtableComponent.getKind() != - VTableComponent::Kind::CK_FunctionPointer) + if (!VtableComponent.isUsedFunctionPointerKind()) continue; - const auto &Method = VtableComponent.getFunctionDecl(); + const CXXMethodDecl *Method = VtableComponent.getFunctionDecl(); if (Method->getCanonicalDecl()->isInlined()) return true; } @@ -1536,7 +1534,7 @@ // then we are safe to emit available_externally copy of vtable. // FIXME we can still emit a copy of the vtable if we // can emit definition of the inline functions. - return !hasAnyVirtualInlineFunction(RD); + return !hasAnyUsedVirtualInlineFunction(RD); } static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, llvm::Value *Ptr, Index: test/CodeGenCXX/vtable-available-externally.cpp =================================================================== --- test/CodeGenCXX/vtable-available-externally.cpp +++ test/CodeGenCXX/vtable-available-externally.cpp @@ -7,6 +7,10 @@ // RUN: FileCheck --check-prefix=CHECK-TEST9 %s < %t.opt // RUN: FileCheck --check-prefix=CHECK-TEST10 %s < %t.opt // RUN: FileCheck --check-prefix=CHECK-TEST11 %s < %t.opt +// RUN: FileCheck --check-prefix=CHECK-TEST12 %s < %t.opt +// RUN: FileCheck --check-prefix=CHECK-TEST13 %s < %t.opt +// RUN: FileCheck --check-prefix=CHECK-TEST14 %s < %t.opt +// RUN: FileCheck --check-prefix=CHECK-TEST15 %s < %t.opt #include @@ -289,3 +293,76 @@ g(d); } } // Test 11 + +namespace Test12 { + +// CHECK-TEST12: @_ZTVN6Test121AE = external unnamed_addr constant +struct A { + virtual void foo(); + virtual ~A() {} +}; +// CHECK-TEST12: @_ZTVN6Test121BE = external unnamed_addr constant +struct B : A { + void foo(); +}; + +void g() { + A a; + a.foo(); + B b; + b.foo(); +} +} + +namespace Test13 { + +// CHECK-TEST13-DAG: @_ZTVN6Test131AE = available_externally unnamed_addr constant +// CHECK-TEST13-DAG: @_ZTVN6Test131BE = external unnamed_addr constant +struct A { + virtual ~A(); +}; +struct B : A { + virtual void f(); + void operator delete(void *); + ~B() {} +}; + +void g() { + A *b = new B; +} +} + +namespace Test14 { + +// CHECK-TEST14: @_ZTVN6Test141AE = available_externally unnamed_addr constant +struct A { + virtual void f(); + void operator delete(void *); + ~A(); +}; + +void g() { + A *b = new A; + delete b; +} +} + +namespace Test15 { +// In this test D's vtable has two slots for function f(), but uses only one, +// so the second slot is set to null. +// CHECK-TEST15: @_ZTVN6Test151DE = available_externally unnamed_addr constant +struct A { virtual void f() {} }; +struct B : virtual A {}; +struct C : virtual A {}; +struct D : B, C { + virtual void g(); + void f(); +}; + +void test() { + D * d = new D; + d->f(); +} +} + +