Skip to content

Commit d8a36b0

Browse files
committed
[ABI] [C++20] [Modules] Don't generate vtable if the class is defined in other module unit
Close llvm#61940. The root cause is that clang will generate vtable as strong symbol now even if the corresponding class is defined in other module units. After I check the wording in Itanium ABI, I find this is not inconsistent. Itanium ABI 5.2.3 (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vague-vtable) says: > The virtual table for a class is emitted in the same object containing > the definition of its key function, i.e. the first non-pure virtual > function that is not inline at the point of class definition. So the current behavior is incorrect. This patch tries to address this. Also I think we need to do a similar change for MSVC ABI. But I don't find the formal wording. So I don't address this in this patch. Reviewed By: rjmccall, iains, dblaikie Differential Revision: https://reviews.llvm.org/D150023
1 parent bd96d7b commit d8a36b0

File tree

2 files changed

+104
-1
lines changed

2 files changed

+104
-1
lines changed

clang/lib/CodeGen/CGVTables.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1172,9 +1172,16 @@ bool CodeGenVTables::isVTableExternal(const CXXRecordDecl *RD) {
11721172
if (!keyFunction)
11731173
return false;
11741174

1175+
const FunctionDecl *Def;
11751176
// Otherwise, if we don't have a definition of the key function, the
11761177
// vtable must be defined somewhere else.
1177-
return !keyFunction->hasBody();
1178+
if (!keyFunction->hasBody(Def))
1179+
return true;
1180+
1181+
assert(Def && "The body of the key function is not assigned to Def?");
1182+
// If the non-inline key function comes from another module unit, the vtable
1183+
// must be defined there.
1184+
return Def->isInAnotherModuleUnit() && !Def->isInlineSpecified();
11781185
}
11791186

11801187
/// Given that we're currently at the end of the translation unit, and
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: cd %t
4+
//
5+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-module-interface \
6+
// RUN: %t/Mod.cppm -o %t/Mod.pcm
7+
//
8+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/Mod.pcm \
9+
// RUN: -emit-llvm -o - | FileCheck %t/Mod.cppm
10+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=Mod=%t/Mod.pcm \
11+
// RUN: %t/Use.cpp -emit-llvm -o - | FileCheck %t/Use.cpp
12+
//
13+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-module-interface \
14+
// RUN: %t/Mod.cppm -o %t/Mod.pcm -DKEY_FUNCTION_INLINE
15+
//
16+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/Mod.pcm \
17+
// RUN: -emit-llvm -o - | FileCheck %t/Mod.cppm -check-prefix=CHECK-INLINE
18+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=Mod=%t/Mod.pcm \
19+
// RUN: %t/Use.cpp -emit-llvm -o - | FileCheck %t/Use.cpp -check-prefix=CHECK-INLINE
20+
//
21+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -emit-module-interface \
22+
// RUN: %t/M-A.cppm -o %t/M-A.pcm
23+
// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -fmodule-file=M:A=%t/M-A.pcm \
24+
// RUN: %t/M-B.cppm -emit-llvm -o - | FileCheck %t/M-B.cppm
25+
26+
//--- Mod.cppm
27+
export module Mod;
28+
29+
export class Base {
30+
public:
31+
virtual ~Base();
32+
};
33+
#ifdef KEY_FUNCTION_INLINE
34+
inline
35+
#endif
36+
Base::~Base() {}
37+
38+
// CHECK: @_ZTVW3Mod4Base = unnamed_addr constant
39+
// CHECK: @_ZTSW3Mod4Base = constant
40+
// CHECK: @_ZTIW3Mod4Base = constant
41+
42+
// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr unnamed_addr constant
43+
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr constant
44+
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr constant
45+
46+
module :private;
47+
int private_use() {
48+
Base base;
49+
return 43;
50+
}
51+
52+
//--- Use.cpp
53+
import Mod;
54+
int use() {
55+
Base* base = new Base();
56+
return 43;
57+
}
58+
59+
// CHECK-NOT: @_ZTSW3Mod4Base = constant
60+
// CHECK-NOT: @_ZTIW3Mod4Base = constant
61+
// CHECK: @_ZTVW3Mod4Base = external unnamed_addr
62+
63+
// CHECK-INLINE: @_ZTVW3Mod4Base = linkonce_odr unnamed_addr constant
64+
// CHECK-INLINE: @_ZTSW3Mod4Base = linkonce_odr constant
65+
// CHECK-INLINE: @_ZTIW3Mod4Base = linkonce_odr constant
66+
67+
// Check the case that the declaration of the key function comes from another
68+
// module unit but the definition of the key function comes from the current
69+
// mdoule unit.
70+
71+
//--- M-A.cppm
72+
export module M:A;
73+
export class C {
74+
public:
75+
virtual ~C();
76+
};
77+
78+
int a_use() {
79+
C c;
80+
return 43;
81+
}
82+
83+
//--- M-B.cppm
84+
export module M:B;
85+
import :A;
86+
87+
C::~C() {}
88+
89+
int b_use() {
90+
C c;
91+
return 43;
92+
}
93+
94+
// CHECK: @_ZTVW1M1C = unnamed_addr constant
95+
// CHECK: @_ZTSW1M1C = constant
96+
// CHECK: @_ZTIW1M1C = constant

0 commit comments

Comments
 (0)