From de8283c542e50b92c87d2dc5307144e603521ebd Mon Sep 17 00:00:00 2001 From: Piotr Padlewski Date: Wed, 19 Aug 2015 20:09:09 +0000 Subject: [PATCH] Generating available_externally vtables bugfix Bugfix revealed in r245264. http://reviews.llvm.org/D12128 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@245489 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/VTableBuilder.h | 50 +++++++----- lib/CodeGen/ItaniumCXXABI.cpp | 10 +-- .../vtable-available-externally.cpp | 77 +++++++++++++++++++ 3 files changed, 112 insertions(+), 25 deletions(-) diff --git a/include/clang/AST/VTableBuilder.h b/include/clang/AST/VTableBuilder.h index 458ae15c59..55d9a76023 100644 --- a/include/clang/AST/VTableBuilder.h +++ b/include/clang/AST/VTableBuilder.h @@ -123,30 +123,50 @@ public: 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 @@ private: } 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 @@ private: } 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); diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 0969275450..2eebf8ee67 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -306,17 +306,15 @@ public: 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; } @@ -1510,7 +1508,7 @@ bool ItaniumCXXABI::canEmitAvailableExternallyVTable( // 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, diff --git a/test/CodeGenCXX/vtable-available-externally.cpp b/test/CodeGenCXX/vtable-available-externally.cpp index ab090936cb..4527efca96 100644 --- a/test/CodeGenCXX/vtable-available-externally.cpp +++ b/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 @@ void g() { 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(); +} +} + + -- 2.40.0