diff --git a/clang/lib/CodeGen/CGVTables.h b/clang/lib/CodeGen/CGVTables.h --- a/clang/lib/CodeGen/CGVTables.h +++ b/clang/lib/CodeGen/CGVTables.h @@ -154,6 +154,10 @@ /// when a vtable may not be dso_local. void GenerateRelativeVTableAlias(llvm::GlobalVariable *VTable, llvm::StringRef AliasNameRef); + + /// Check if we can place the vtable in a comdat group under the relative + /// layout. + bool CanPlaceRelativeVTableInComdat(const llvm::GlobalVariable *VTable); }; } // end namespace CodeGen diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp --- a/clang/lib/CodeGen/CGVTables.cpp +++ b/clang/lib/CodeGen/CGVTables.cpp @@ -936,9 +936,19 @@ if (UsingRelativeLayout && !VTable->isDSOLocal()) GenerateRelativeVTableAlias(VTable, OutName); + if (CanPlaceRelativeVTableInComdat(VTable)) + VTable->setComdat(CGM.getModule().getOrInsertComdat(VTable->getName())); + return VTable; } +bool CodeGenVTables::CanPlaceRelativeVTableInComdat( + const llvm::GlobalVariable *VTable) { + return CGM.supportsCOMDAT() && getItaniumVTableContext().isRelativeLayout() && + !VTable->hasLocalLinkage() && !VTable->isDeclarationForLinker() && + !VTable->hasComdat(); +} + // If the VTable is not dso_local, then we will not be able to indicate that // the VTable does not need a relocation and move into rodata. A frequent // time this can occur is for classes that should be made public from a DSO diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -1772,6 +1772,9 @@ if (VTContext.isRelativeLayout() && !VTable->isDSOLocal()) CGVT.GenerateRelativeVTableAlias(VTable, VTable->getName()); + + if (CGVT.CanPlaceRelativeVTableInComdat(VTable)) + VTable->setComdat(CGM.getModule().getOrInsertComdat(VTable->getName())); } bool ItaniumCXXABI::isVirtualOffsetNeededForVTableField( diff --git a/clang/test/CodeGenCXX/RelativeVTablesABI/vtable-in-comdat.cpp b/clang/test/CodeGenCXX/RelativeVTablesABI/vtable-in-comdat.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/RelativeVTablesABI/vtable-in-comdat.cpp @@ -0,0 +1,24 @@ +// Check that all vtables under the relative ABI are placed in a comdat group. +// This is necessary because if the vtable happens to fit in a mergable +// constants section, then the LLVM backend will not place the symbol in its own +// unique section even if -fdata-sections is enabled. (That is, in the object +// file the vtable is defined, it will be in section .rodata.cst{N} instead of +// something like .rodata.{vtable}). This can cause vtables to be retained that +// normally would be collected by --gc-sections. Placing them in a comdat group +// can guarantee that they will be placed in a unique section group and garbage +// collected. + +// RUN: %clang_cc1 %s -triple=aarch64-unknown-fuchsia -S -o - -emit-llvm -fexperimental-relative-c++-abi-vtables -fno-rtti | FileCheck %s + +// This will result in a vtable of size 16, which would normally be placed in +// .rodata.cst16. +class A { +public: + virtual void func(); + virtual void func2(); +}; + +void A::func() {} +void A::func2() {} + +// CHECK: @_ZTV1A = {{.*}}, comdat, align 4