Skip to content

Commit 53d5ffe

Browse files
authored
[clang] Check inline defs when emitting speculative vtable (#100785)
Clang should only emit an available_externally vtable when there are no unused virtual inline functions. Currently, if such such a function is declared without inline inside the class, but is defined inline outside the class, Clang might emit the vtable as available_externally. This happens because Clang only considers the declarations of vtable entries, but not the definitions. This patch addresses this by inspecting the definitions in addition to the declarations.
1 parent 5515b08 commit 53d5ffe

File tree

2 files changed

+34
-10
lines changed

2 files changed

+34
-10
lines changed

clang/lib/CodeGen/ItaniumCXXABI.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,10 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
442442
continue;
443443

444444
const CXXMethodDecl *Method = VtableComponent.getFunctionDecl();
445-
if (!Method->getCanonicalDecl()->isInlined())
445+
const FunctionDecl *FD = Method->getDefinition();
446+
const bool IsInlined =
447+
Method->getCanonicalDecl()->isInlined() || (FD && FD->isInlined());
448+
if (!IsInlined)
446449
continue;
447450

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

2282-
// If we don't have any not emitted inline virtual function then we are safe
2283-
// to emit an available_externally copy of vtable.
2285+
// A speculative vtable can only be generated if all virtual inline functions
2286+
// defined by this class are emitted. The vtable in the final program contains
2287+
// for each virtual inline function not used in the current TU a function that
2288+
// is equivalent to the unused function. The function in the actual vtable
2289+
// does not have to be declared under the same symbol (e.g., a virtual
2290+
// destructor that can be substituted with its base class's destructor). Since
2291+
// inline functions are emitted lazily and this emissions does not account for
2292+
// speculative emission of a vtable, we might generate a speculative vtable
2293+
// with references to inline functions that are not emitted under that name.
2294+
// This can lead to problems when devirtualizing a call to such a function,
2295+
// that result in linking errors. Hence, if there are any unused virtual
2296+
// inline function, we cannot emit the speculative vtable.
22842297
// FIXME we can still emit a copy of the vtable if we
22852298
// can emit definition of the inline functions.
22862299
if (hasAnyUnusedVirtualInlineFunction(RD))

clang/test/CodeGenCXX/vtable-available-externally.cpp

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,28 +250,39 @@ struct C : A {
250250
virtual void car();
251251
};
252252

253+
// Inline definition outside body, so we can't emit vtable available_externally
254+
// (see previous).
255+
// CHECK-TEST10-DAG: @_ZTVN6Test101FE = external unnamed_addr constant
256+
struct F : A {
257+
void foo();
258+
virtual void cat(); // inline outside body
259+
};
260+
inline void F::cat() {}
261+
253262
// no key function, vtable will be generated everywhere it will be used
254263
// CHECK-TEST10-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
255264
// CHECK-FORCE-EMIT-DAG: @_ZTVN6Test101EE = linkonce_odr unnamed_addr constant
256265

257266
struct E : A {};
258267

259-
void g(A& a) {
268+
void h(A& a) {
260269
a.foo();
261270
a.bar();
262271
}
263272

264-
void f() {
273+
void g() {
265274
A a;
266-
g(a);
275+
h(a);
267276
B b;
268-
g(b);
277+
h(b);
269278
C c;
270-
g(c);
279+
h(c);
271280
D d;
272-
g(d);
281+
h(d);
273282
E e;
274-
g(e);
283+
h(e);
284+
F f;
285+
h(f);
275286
}
276287

277288
} // Test10

0 commit comments

Comments
 (0)