Skip to content

Commit

Permalink
[clang] Emit type metadata on available_externally vtables for WPD
Browse files Browse the repository at this point in the history
When WPD is enabled, via WholeProgramVTables, emit type metadata for
available_externally vtables. Additionally, add the vtables to the
llvm.compiler.used global so that they are not prematurely eliminated
(before *LTO analysis).

This is needed to avoid devirtualizing calls to a function overriding a
class defined in a header file but with a strong definition in a shared
library. Without type metadata on the available_externally vtables from
the header, the WPD analysis never sees what a derived class is
overriding. Even if the available_externally base class functions are
pure virtual, because shared library definitions are already treated
conservatively (committed patches D91583, D96721, and D96722) we will
not devirtualize, which would be unsafe since the library might contain
overrides that aren't visible to the LTO unit.

An example is std::error_category, which is overridden in LLVM
and causing failures after a self build with WPD enabled, because
libstdc++ contains hidden overrides of the virtual base class methods.

Differential Revision: https://reviews.llvm.org/D96919
  • Loading branch information
teresajohnson committed Feb 19, 2021
1 parent efc8f33 commit 0923a60
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 1 deletion.
16 changes: 15 additions & 1 deletion clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1767,8 +1767,22 @@ void ItaniumCXXABI::emitVTableDefinitions(CodeGenVTables &CGVT,
DC->getParent()->isTranslationUnit())
EmitFundamentalRTTIDescriptors(RD);

if (!VTable->isDeclarationForLinker())
// Always emit type metadata on non-available_externally definitions, and on
// available_externally definitions if we are performing whole program
// devirtualization. For WPD we need the type metadata on all vtable
// definitions to ensure we associate derived classes with base classes
// defined in headers but with a strong definition only in a shared library.
if (!VTable->isDeclarationForLinker() ||
CGM.getCodeGenOpts().WholeProgramVTables) {
CGM.EmitVTableTypeMetadata(RD, VTable, VTLayout);
// For available_externally definitions, add the vtable to
// @llvm.compiler.used so that it isn't deleted before whole program
// analysis.
if (VTable->isDeclarationForLinker()) {
assert(CGM.getCodeGenOpts().WholeProgramVTables);
CGM.addCompilerUsedGlobal(VTable);
}
}

if (VTContext.isRelativeLayout() && !VTable->isDSOLocal())
CGVT.GenerateRelativeVTableAlias(VTable, VTable->getName());
Expand Down
25 changes: 25 additions & 0 deletions clang/test/CodeGenCXX/type-metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// Tests for the whole-program-vtables feature:
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility hidden -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM --check-prefix=TT-ITANIUM %s
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=ITANIUM-DEFAULTVIS --check-prefix=TT-ITANIUM %s
// RUN: %clang_cc1 -O2 -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM-OPT %s
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=MS --check-prefix=TT-MS %s

// Tests for cfi + whole-program-vtables:
Expand Down Expand Up @@ -79,6 +80,13 @@
// ITANIUM-DIAG-SAME: !type [[ALL16]]
// ITANIUM-SAME: !type [[FAF16:![0-9]+]]

// ITANIUM: @_ZTVN5test31EE = external unnamed_addr constant
// ITANIUM-DEFAULTVIS: @_ZTVN5test31EE = external unnamed_addr constant
// ITANIUM-OPT: @_ZTVN5test31EE = available_externally unnamed_addr constant {{[^!]*}},
// ITANIUM-OPT-SAME: !type [[E16:![0-9]+]],
// ITANIUM-OPT-SAME: !type [[EF16:![0-9]+]]
// ITANIUM-OPT: @llvm.compiler.used = appending global [1 x i8*] [i8* bitcast ({ [3 x i8*] }* @_ZTVN5test31EE to i8*)]

// MS: comdat($"??_7A@@6B@"), !type [[A8:![0-9]+]]
// MS: comdat($"??_7B@@6B0@@"), !type [[B8:![0-9]+]]
// MS: comdat($"??_7B@@6BA@@@"), !type [[A8]]
Expand Down Expand Up @@ -253,6 +261,20 @@ void f(D *d) {

}

namespace test3 {
// All virtual functions are outline, so we can assume that it will
// be generated in translation unit where foo is defined.
struct E {
virtual void foo();
};

void g() {
E e;
e.foo();
}

} // Test9

// ITANIUM: [[A16]] = !{i64 16, !"_ZTS1A"}
// ITANIUM-DIAG: [[ALL16]] = !{i64 16, !"all-vtables"}
// ITANIUM: [[AF16]] = !{i64 16, !"_ZTSM1AFvvE.virtual"}
Expand Down Expand Up @@ -286,6 +308,9 @@ void f(D *d) {
// ITANIUM: [[FAF16]] = !{i64 16, [[FAF_ID:![0-9]+]]}
// ITANIUM: [[FAF_ID]] = distinct !{}

// ITANIUM-OPT: [[E16]] = !{i64 16, !"_ZTSN5test31EE"}
// ITANIUM-OPT: [[EF16]] = !{i64 16, !"_ZTSMN5test31EEFvvE.virtual"}

// MS: [[A8]] = !{i64 8, !"?AUA@@"}
// MS: [[B8]] = !{i64 8, !"?AUB@@"}
// MS: [[D8]] = !{i64 8, [[D_ID:![0-9]+]]}
Expand Down

0 comments on commit 0923a60

Please sign in to comment.