Index: include/clang/AST/VTableBuilder.h =================================================================== --- include/clang/AST/VTableBuilder.h +++ include/clang/AST/VTableBuilder.h @@ -122,7 +122,7 @@ } const CXXRecordDecl *getRTTIDecl() const { - assert(getKind() == CK_RTTI && "Invalid component kind!"); + assert(isRTTIKind() && "Invalid component kind!"); return reinterpret_cast(getPointer()); } @@ -153,6 +153,8 @@ return isFunctionPointerKind(getKind()); } + bool isRTTIKind() const { return isRTTIKind(getKind()); } + private: static bool isFunctionPointerKind(Kind ComponentKind) { return isUsedFunctionPointerKind(ComponentKind) || @@ -166,6 +168,9 @@ return ComponentKind == CK_CompleteDtorPointer || ComponentKind == CK_DeletingDtorPointer; } + static bool isRTTIKind(Kind ComponentKind) { + return ComponentKind == CK_RTTI; + } VTableComponent(Kind ComponentKind, CharUnits Offset) { assert((ComponentKind == CK_VCallOffset || @@ -178,7 +183,7 @@ } VTableComponent(Kind ComponentKind, uintptr_t Ptr) { - assert((ComponentKind == CK_RTTI || isFunctionPointerKind(ComponentKind)) && + assert((isRTTIKind(ComponentKind) || isFunctionPointerKind(ComponentKind)) && "Invalid component kind!"); assert((Ptr & 7) == 0 && "Pointer not sufficiently aligned!"); Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2036,14 +2036,13 @@ // first, it's incorrect for classes with virtual bases, and second, we're // about to overwrite the vptrs anyway. // We also have to make sure if we can refer to vtable: - // - If vtable is external then it's safe to use it (for available_externally - // CGVTables will make sure if it can emit it). // - Otherwise we can refer to vtable if it's safe to speculatively emit. - // FIXME: If vtable is used by ctor/dtor, we are always safe to refer to it. + // FIXME: If vtable is used by ctor/dtor, or if vtable is external and we are + // sure that definition of vtable is not hidden, + // then we are always safe to refer to it. if (CGM.getCodeGenOpts().OptimizationLevel > 0 && ClassDecl->isDynamicClass() && Type != Ctor_Base && - (CGM.getVTables().isVTableExternal(ClassDecl) || - CGM.getCXXABI().canSpeculativelyEmitVTable(ClassDecl))) + CGM.getCXXABI().canSpeculativelyEmitVTable(ClassDecl)) EmitVTableAssumptionLoads(ClassDecl, This); } Index: lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- lib/CodeGen/ItaniumCXXABI.cpp +++ lib/CodeGen/ItaniumCXXABI.cpp @@ -386,6 +386,25 @@ } return false; } + + bool isVTableHidden(const CXXRecordDecl *RD) const { + const auto &VtableLayout = + CGM.getItaniumVTableContext().getVTableLayout(RD); + + for (const auto &VtableComponent : VtableLayout.vtable_components()) { + if (VtableComponent.isRTTIKind()) { + const CXXRecordDecl *RTTIDecl = VtableComponent.getRTTIDecl(); + if (RTTIDecl->getVisibility() == Visibility::HiddenVisibility) + return true; + } else if (VtableComponent.isUsedFunctionPointerKind()) { + const CXXMethodDecl *Method = VtableComponent.getFunctionDecl(); + if (Method->getVisibility() == Visibility::HiddenVisibility && + !Method->isDefined()) + return true; + } + } + return false; + } }; class ARMCXXABI : public ItaniumCXXABI { @@ -1615,11 +1634,11 @@ if (CGM.getLangOpts().AppleKext) return false; - // If we don't have any inline virtual functions, + // If we don't have any inline virtual functions, and if vtable is not hidden, // 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 !hasAnyUsedVirtualInlineFunction(RD); + return !hasAnyUsedVirtualInlineFunction(RD) && !isVTableHidden(RD); } static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, Address InitialPtr, Index: test/CodeGenCXX/vtable-assume-load.cpp =================================================================== --- test/CodeGenCXX/vtable-assume-load.cpp +++ test/CodeGenCXX/vtable-assume-load.cpp @@ -9,6 +9,7 @@ // RUN: FileCheck --check-prefix=CHECK6 --input-file=%t.ll %s // RUN: FileCheck --check-prefix=CHECK7 --input-file=%t.ll %s // RUN: FileCheck --check-prefix=CHECK8 --input-file=%t.ll %s +// RUN: FileCheck --check-prefix=CHECK9 --input-file=%t.ll %s namespace test1 { struct A { @@ -174,7 +175,6 @@ } // testMS namespace test6 { -// CHECK6: @_ZTVN5test61AE = external struct A { A(); virtual void foo(); @@ -183,10 +183,11 @@ struct B : A { B(); }; -// Because A's vtable is external, it's safe to generate assumption loads. +// FIXME: Because A's vtable is external, and no virtual functions are hidden, +// it's safe to generate assumption loads. // CHECK6-LABEL: define void @_ZN5test61gEv() // CHECK6: call void @_ZN5test61AC1Ev( -// CHECK6: call void @llvm.assume( +// CHECK6-NOT: call void @llvm.assume( // We can't emit assumption loads for B, because if we would refer to vtable // it would refer to functions that will not be able to find (like implicit @@ -243,7 +244,6 @@ }; inline void C::bar() {} -// CHECK8-DAG: @_ZTVN5test81DE = external unnamed_addr constant struct D : A { D(); void foo(); @@ -275,8 +275,9 @@ c.bar(); } +// FIXME: We could generate assumption loads here. // CHECK8-LABEL: define void @_ZN5test81dEv() -// CHECK8: call void @llvm.assume( +// CHECK8-NOT: call void @llvm.assume( // CHECK8-LABEL: } void d() { D d; @@ -291,3 +292,21 @@ e.bar(); } } + +namespace test9 { + +struct S { + __attribute__((visibility("hidden"))) S(); + virtual void doStuff(); +}; + +// CHECK9-LABEL: define void @_ZN5test94testEv() +// CHECK9-NOT: @llvm.assume( +// CHECK9: } +void test() { + S *s = new S(); + s->doStuff(); + delete s; +} +} + Index: test/CodeGenCXX/vtable-available-externally.cpp =================================================================== --- test/CodeGenCXX/vtable-available-externally.cpp +++ test/CodeGenCXX/vtable-available-externally.cpp @@ -11,6 +11,7 @@ // 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 +// RUN: FileCheck --check-prefix=CHECK-TEST16 %s < %t.opt #include @@ -365,3 +366,28 @@ } } +namespace Test16 { +// S has virtual method that is hidden, because of it we can't +// generate available_externally vtable for it. +// CHECK-TEST16-DAG: @_ZTVN6Test161SE = external unnamed_addr constant +// CHECK-TEST16-DAG: @_ZTVN6Test162S2E = available_externally + +struct S { + __attribute__((visibility("hidden"))) virtual void doStuff(); +}; + +struct S2 { + virtual void doStuff(); + __attribute__((visibility("hidden"))) void unused(); + +}; + +void test() { + S *s = new S; + s->doStuff(); + + S2 *s2 = new S2; + s2->doStuff(); +} +} +