Skip to content

[clang] Check inline defs when emitting speculative vtable #100785

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,10 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
continue;

const CXXMethodDecl *Method = VtableComponent.getFunctionDecl();
if (!Method->getCanonicalDecl()->isInlined())
const FunctionDecl *FD = Method->getDefinition();
const bool IsInlined =
Method->getCanonicalDecl()->isInlined() || (FD && FD->isInlined());
if (!IsInlined)
continue;

StringRef Name = CGM.getMangledName(VtableComponent.getGlobalDecl());
Expand Down Expand Up @@ -2279,8 +2282,18 @@ bool ItaniumCXXABI::canSpeculativelyEmitVTableAsBaseClass(
if (CGM.getCodeGenOpts().ForceEmitVTables)
return true;

// If we don't have any not emitted inline virtual function then we are safe
// to emit an available_externally copy of vtable.
// A speculative vtable can only be generated if all virtual inline functions
// defined by this class are emitted. The vtable in the final program contains
// for each virtual inline function not used in the current TU a function that
// is equivalent to the unused function. The function in the actual vtable
// does not have to be declared under the same symbol (e.g., a virtual
// destructor that can be substituted with its base class's destructor). Since
// inline functions are emitted lazily and this emissions does not account for
// speculative emission of a vtable, we might generate a speculative vtable
// with references to inline functions that are not emitted under that name.
// This can lead to problems when devirtualizing a call to such a function,
// that result in linking errors. Hence, if there are any unused virtual
// inline function, we cannot emit the speculative vtable.
// FIXME we can still emit a copy of the vtable if we
// can emit definition of the inline functions.
if (hasAnyUnusedVirtualInlineFunction(RD))
Expand Down
25 changes: 18 additions & 7 deletions clang/test/CodeGenCXX/vtable-available-externally.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,28 +250,39 @@ struct C : A {
virtual void car();
};

// Inline definition outside body, so we can't emit vtable available_externally
// (see previous).
// CHECK-TEST10-DAG: @_ZTVN6Test101FE = external unnamed_addr constant
struct F : A {
void foo();
virtual void cat(); // inline outside body
};
inline void F::cat() {}

// no key function, vtable will be generated everywhere it will be used
// CHECK-TEST10-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant

struct E : A {};

void g(A& a) {
void h(A& a) {
a.foo();
a.bar();
}

void f() {
void g() {
A a;
g(a);
h(a);
B b;
g(b);
h(b);
C c;
g(c);
h(c);
D d;
g(d);
h(d);
E e;
g(e);
h(e);
F f;
h(f);
}

} // Test10
Expand Down
Loading