Index: cfe/trunk/include/clang/AST/VTableBuilder.h =================================================================== --- cfe/trunk/include/clang/AST/VTableBuilder.h +++ cfe/trunk/include/clang/AST/VTableBuilder.h @@ -154,6 +154,27 @@ bool isRTTIKind() const { return isRTTIKind(getKind()); } + GlobalDecl getGlobalDecl() const { + assert(isUsedFunctionPointerKind() && + "GlobalDecl can be created only from virtual function"); + + auto *DtorDecl = dyn_cast(getFunctionDecl()); + switch (getKind()) { + case CK_FunctionPointer: + return GlobalDecl(getFunctionDecl()); + case CK_CompleteDtorPointer: + return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Complete); + case CK_DeletingDtorPointer: + return GlobalDecl(DtorDecl, CXXDtorType::Dtor_Deleting); + case CK_VCallOffset: + case CK_VBaseOffset: + case CK_OffsetToTop: + case CK_RTTI: + case CK_UnusedFunctionPointer: + llvm_unreachable("Only function pointers kinds"); + } + } + private: static bool isFunctionPointerKind(Kind ComponentKind) { return isUsedFunctionPointerKind(ComponentKind) || Index: cfe/trunk/lib/CodeGen/CGVTables.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGVTables.cpp +++ cfe/trunk/lib/CodeGen/CGVTables.cpp @@ -901,6 +901,8 @@ for (const CXXRecordDecl *RD : DeferredVTables) if (shouldEmitVTableAtEndOfTranslationUnit(*this, RD)) VTables.GenerateClassData(RD); + else if (shouldOpportunisticallyEmitVTables()) + OpportunisticVTables.push_back(RD); assert(savedSize == DeferredVTables.size() && "deferred extra vtables during vtable emission?"); Index: cfe/trunk/lib/CodeGen/CodeGenModule.h =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.h +++ cfe/trunk/lib/CodeGen/CodeGenModule.h @@ -341,6 +341,9 @@ /// A queue of (optional) vtables to consider emitting. std::vector DeferredVTables; + /// A queue of (optional) vtables that may be emitted opportunistically. + std::vector OpportunisticVTables; + /// List of global values which are required to be present in the object file; /// bitcast to i8*. This is used for forcing visibility of symbols which may /// otherwise be optimized out. @@ -450,7 +453,7 @@ bool isTriviallyRecursive(const FunctionDecl *F); bool shouldEmitFunction(GlobalDecl GD); - + bool shouldOpportunisticallyEmitVTables(); /// Map used to be sure we don't emit the same CompoundLiteral twice. llvm::DenseMap EmittedCompoundLiterals; @@ -1278,6 +1281,12 @@ /// Emit any needed decls for which code generation was deferred. void EmitDeferred(); + /// Try to emit external vtables as available_externally if they have emitted + /// all inlined virtual functions. It runs after EmitDeferred() and therefore + /// is not allowed to create new references to things that need to be emitted + /// lazily. + void EmitVTablesOpportunistically(); + /// Call replaceAllUsesWith on all pairs in Replacements. void applyReplacements(); Index: cfe/trunk/lib/CodeGen/CodeGenModule.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp @@ -382,6 +382,7 @@ void CodeGenModule::Release() { EmitDeferred(); + EmitVTablesOpportunistically(); applyGlobalValReplacements(); applyReplacements(); checkAliases(); @@ -1386,6 +1387,24 @@ } } +void CodeGenModule::EmitVTablesOpportunistically() { + // Try to emit external vtables as available_externally if they have emitted + // all inlined virtual functions. It runs after EmitDeferred() and therefore + // is not allowed to create new references to things that need to be emitted + // lazily. Note that it also uses fact that we eagerly emitting RTTI. + + assert(OpportunisticVTables.empty() || shouldOpportunisticallyEmitVTables() && + "Only emit opportunistic vtables with optimizations"); + + for (const CXXRecordDecl *RD : OpportunisticVTables) { + assert(getVTables().isVTableExternal(RD) && + "This queue should only contain external vtables"); + if (getCXXABI().canSpeculativelyEmitVTable(RD)) + VTables.GenerateClassData(RD); + } + OpportunisticVTables.clear(); +} + void CodeGenModule::EmitGlobalAnnotations() { if (Annotations.empty()) return; @@ -1906,6 +1925,10 @@ return !isTriviallyRecursive(F); } +bool CodeGenModule::shouldOpportunisticallyEmitVTables() { + return CodeGenOpts.OptimizationLevel > 0; +} + void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -366,20 +366,30 @@ void emitCXXStructor(const CXXMethodDecl *MD, StructorType Type) override; private: - bool hasAnyVirtualInlineFunction(const CXXRecordDecl *RD) const { - const auto &VtableLayout = - CGM.getItaniumVTableContext().getVTableLayout(RD); - - for (const auto &VtableComponent : VtableLayout.vtable_components()) { - // Skip empty slot. - if (!VtableComponent.isUsedFunctionPointerKind()) - continue; - - const CXXMethodDecl *Method = VtableComponent.getFunctionDecl(); - if (Method->getCanonicalDecl()->isInlined()) - return true; - } - return false; + bool hasAnyUnusedVirtualInlineFunction(const CXXRecordDecl *RD) const { + const auto &VtableLayout = + CGM.getItaniumVTableContext().getVTableLayout(RD); + + for (const auto &VtableComponent : VtableLayout.vtable_components()) { + // Skip empty slot. + if (!VtableComponent.isUsedFunctionPointerKind()) + continue; + + const CXXMethodDecl *Method = VtableComponent.getFunctionDecl(); + if (!Method->getCanonicalDecl()->isInlined()) + continue; + + StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl()); + auto *Entry = CGM.GetGlobalValue(Name); + // This checks if virtual inline function has already been emitted. + // Note that it is possible that this inline function would be emitted + // after trying to emit vtable speculatively. Because of this we do + // an extra pass after emitting all deferred vtables to find and emit + // these vtables opportunistically. + if (!Entry || Entry->isDeclaration()) + return true; + } + return false; } bool isVTableHidden(const CXXRecordDecl *RD) const { @@ -1687,11 +1697,11 @@ if (CGM.getLangOpts().AppleKext) return false; - // 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. + // If we don't have any not emitted inline virtual function, 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 !hasAnyVirtualInlineFunction(RD) && !isVTableHidden(RD); + return !hasAnyUnusedVirtualInlineFunction(RD) && !isVTableHidden(RD); } static llvm::Value *performTypeAdjustment(CodeGenFunction &CGF, Address InitialPtr, @@ -2576,6 +2586,9 @@ if (!GV) { // Create a new global variable. + // Note for the future: If we would ever like to do deferred emission of + // RTTI, check if emitting vtables opportunistically need any adjustment. + GV = new llvm::GlobalVariable(CGM.getModule(), CGM.Int8PtrTy, /*Constant=*/true, llvm::GlobalValue::ExternalLinkage, nullptr, Index: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp @@ -3756,6 +3756,9 @@ if (llvm::GlobalVariable *GV = CGM.getModule().getNamedGlobal(MangledName)) return llvm::ConstantExpr::getBitCast(GV, CGM.Int8PtrTy); + // Note for the future: If we would ever like to do deferred emission of + // RTTI, check if emitting vtables opportunistically need any adjustment. + // Compute the fields for the TypeDescriptor. SmallString<256> TypeInfoString; { Index: cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp +++ cfe/trunk/test/CodeGenCXX/vtable-available-externally.cpp @@ -12,6 +12,7 @@ // 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 +// RUN: FileCheck --check-prefix=CHECK-TEST17 %s < %t.opt #include @@ -274,8 +275,8 @@ virtual D& operator=(const D&); }; -// Cannot emit B's vtable available_externally, because we cannot create -// a reference to the inline virtual B::operator= function. +// Cannot emit D's vtable available_externally, because we cannot create +// a reference to the inline virtual D::operator= function. // CHECK-TEST11: @_ZTVN6Test111DE = external unnamed_addr constant struct D : C { virtual void key(); @@ -391,3 +392,30 @@ } } +namespace Test17 { +// This test checks if we emit vtables opportunistically. +// CHECK-TEST17-DAG: @_ZTVN6Test171AE = available_externally +// CHECK-TEST17-DAG: @_ZTVN6Test171BE = external + +struct A { + virtual void key(); + virtual void bar() {} +}; + +// We won't gonna use deleting destructor for this type, which will disallow +// emitting vtable as available_externally +struct B { + virtual void key(); + virtual ~B() {} +}; + +void testcaseA() { + A a; + a.bar(); // this forces to emit definition of bar +} + +void testcaseB() { + B b; // This only forces emitting of complete object destructor +} + +} // namespace Test17 Index: cfe/trunk/test/CodeGenCXX/vtable-linkage.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/vtable-linkage.cpp +++ cfe/trunk/test/CodeGenCXX/vtable-linkage.cpp @@ -145,12 +145,14 @@ // F is an explicit template instantiation declaration without a // key function, so its vtable should have external linkage. // CHECK-DAG: @_ZTV1FIiE = external unnamed_addr constant -// CHECK-OPT-DAG: @_ZTV1FIiE = external unnamed_addr constant +// CHECK-OPT-DAG: @_ZTV1FIiE = available_externally unnamed_addr constant // E is an explicit template instantiation declaration. It has a // key function is not instantiated, so we know that vtable definition // will be generated in TU where key function will be defined -// so we can mark it as available_externally (only with optimizations) +// so we can mark it as external (without optimizations) and +// available_externally (with optimizations) because all of the inline +// virtual functions have been emitted. // CHECK-DAG: @_ZTV1EIiE = external unnamed_addr constant // CHECK-OPT-DAG: @_ZTV1EIiE = available_externally unnamed_addr constant