Skip to content

Commit d99033e

Browse files
[LTO][WPD] Suppress WPD on a class if the LTO unit doesn't have the prevailing definition of this class (#131721)
Before this patch, whole program devirtualization is suppressed on a class if any superclass is visible to regular object files, by recording the class GUID in `VisibleToRegularObjSymbols`. This patch suppresses whole program devirtualization on a class if the LTO unit doesn't have the prevailing definition of this class (e.g., the prevailing definition is in a shared library) Implementation summaries: 1. In llvm/lib/LTO/LTO.cpp, `IsVisibleToRegularObj` is updated to look at the global resolution's `IsPrevailing` bit for ThinLTO and regularLTO. 2. In llvm/tools/llvm-lto2/llvm-lto2.cpp, - three command line options are added so `llvm-lto2` can override `Conf.HasWholeProgramVisibility`, `Conf.ValidateAllVtablesHaveTypeInfos` and `Conf.AllVtablesHaveTypeInfos`. The test case is reduced from a small C++ program (main.cc, lib.cc/h pasted below in [1]). To reproduce the program failure without this patch, compile lib.cc into a shared library, and provide it to a ThinLTO build of main.cc (commands are pasted in [2]). [1] * lib.h ``` #include <cstdio> class Derived { public: void dispatch(); virtual void print(); virtual void sum(); }; void Derived::dispatch() { static_cast<Derived*>(this)->print(); static_cast<Derived*>(this)->sum(); } void Derived::sum() { printf("Derived::sum\n"); } __attribute__((noinline)) void* create(int i); __attribute__((noinline)) void* getPtr(int i); ``` * lib.cc ``` #include "lib.h" #include <cstdio> #include <iostream> class Derived2 : public Derived { public: void print() override { printf("DerivedSharedLib\n"); } void sum() override { printf("DerivedSharedLib::sum\n"); } }; void Derived::print() { printf("Derived\n"); } __attribute__((noinline)) void* create(int i) { if (i & 1) return new Derived2(); return new Derived(); } ``` * main.cc ``` cat main.cc #include "lib.h" class DerivedN : public Derived { public: }; __attribute__((noinline)) void* getPtr(int x) { return new DerivedN(); } int main() { Derived*b = static_cast<Derived*>(create(201)); b->dispatch(); delete b; Derived* a = static_cast<Derived*>(getPtr(202)); a->dispatch(); delete a; return 0; } ``` [2] ``` # compile lib.o in a shared library. $ ./bin/clang++ -O2 -fPIC -c lib.cc -o lib.o $ ./bin/clang++ -shared -o libdata.so lib.o # Provide the shared library in `-ldata` $ ./bin/clang++ -v -g -ldata --save-temps -fno-discard-value-names -Wl,-mllvm,-print-before=wholeprogramdevirt -Wl,-mllvm,-wholeprogramdevirt-check=trap -Rpass=wholeprogramdevirt -Wl,--lto-whole-program-visibility -Wl,--lto-validate-all-vtables-have-type-infos -mllvm -disable-icp=true -Wl,-mllvm,-disable-icp=false -flto=thin -fwhole-program-vtables -fno-split-lto-unit -fuse-ld=lld main.cc -L . -o main >/tmp/wholeprogramdevirt.ir 2>&1 # Run the program hits a segmentation fault with `-Wl,-mllvm,-wholeprogramdevirt-check=trap` $ LD_LIBRARY_PATH=. ./main DerivedSharedLib Trace/breakpoint trap (core dumped) ```
1 parent d0d33d2 commit d99033e

File tree

3 files changed

+180
-2
lines changed

3 files changed

+180
-2
lines changed

llvm/lib/LTO/LTO.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,7 +1314,8 @@ Error LTO::runRegularLTO(AddStreamFn AddStream) {
13141314
// expected to be handled separately.
13151315
auto IsVisibleToRegularObj = [&](StringRef name) {
13161316
auto It = GlobalResolutions->find(name);
1317-
return (It == GlobalResolutions->end() || It->second.VisibleOutsideSummary);
1317+
return (It == GlobalResolutions->end() ||
1318+
It->second.VisibleOutsideSummary || !It->second.Prevailing);
13181319
};
13191320

13201321
// If allowed, upgrade public vcall visibility metadata to linkage unit
@@ -1905,7 +1906,7 @@ Error LTO::runThinLTO(AddStreamFn AddStream, FileCache Cache,
19051906
auto IsVisibleToRegularObj = [&](StringRef name) {
19061907
auto It = GlobalResolutions->find(name);
19071908
return (It == GlobalResolutions->end() ||
1908-
It->second.VisibleOutsideSummary);
1909+
It->second.VisibleOutsideSummary || !It->second.Prevailing);
19091910
};
19101911

19111912
getVisibleToRegularObjVtableGUIDs(ThinLTO.CombinedIndex,
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
; RUN: rm -rf %t && mkdir %t && cd %t
2+
3+
; Tests that devirtualization is suppressed on a class when the LTO unit doesn't
4+
; have the prevailing definition of the base class.
5+
6+
; Generate unsplit module with summary for ThinLTO index-based WPD.
7+
; RUN: opt -thinlto-bc -o summary.o %s
8+
9+
; Index based WPD
10+
; The callsite inside @_ZN4Base8dispatchEv gets devirtualized when symbol
11+
; resolution shows there is a prevailing definition of `_ZTI7Derived` in the
12+
; LTO unit.
13+
; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \
14+
; RUN: -thinlto-threads=1 \
15+
; RUN: -o tmp \
16+
; RUN: --whole-program-visibility-enabled-in-lto=true \
17+
; RUN: --validate-all-vtables-have-type-infos=true \
18+
; RUN: --all-vtables-have-type-infos=true \
19+
; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \
20+
; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \
21+
; RUN: -r=summary.o,_ZTV8DerivedN,p \
22+
; RUN: -r=summary.o,_ZTI8DerivedN,p \
23+
; RUN: -r=summary.o,_ZTS8DerivedN,p \
24+
; RUN: -r=summary.o,_ZTI7Derived,p \
25+
; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK
26+
27+
28+
; Index based WPD
29+
; The callsite inside @_ZN4Base8dispatchEv remains indirect and not de-virtualized
30+
; when symbol resolution shows there isn't a prevailing definition of
31+
; `_ZTI7Derived` in the LTO unit.
32+
; RUN: llvm-lto2 run summary.o -save-temps -pass-remarks=. \
33+
; RUN: -thinlto-threads=1 \
34+
; RUN: -o tmp \
35+
; RUN: --whole-program-visibility-enabled-in-lto=true \
36+
; RUN: --validate-all-vtables-have-type-infos=true \
37+
; RUN: --all-vtables-have-type-infos=true \
38+
; RUN: -r=summary.o,_ZN4Base8dispatchEv,px \
39+
; RUN: -r=summary.o,_ZN8DerivedN5printEv,px \
40+
; RUN: -r=summary.o,_ZTV8DerivedN,p \
41+
; RUN: -r=summary.o,_ZTI8DerivedN,p \
42+
; RUN: -r=summary.o,_ZTS8DerivedN,p \
43+
; RUN: -r=summary.o,_ZTI7Derived, \
44+
; RUN: 2>&1 | FileCheck %s --allow-empty --implicit-check-not='single-impl: devirtualized a call to'
45+
46+
; Repeat the above tests for WPD in hybrid LTO.
47+
; RUN: opt --thinlto-bc --thinlto-split-lto-unit -o hybrid.o %s
48+
49+
; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \
50+
; RUN: -thinlto-threads=1 \
51+
; RUN: -o hybrid-tmp \
52+
; RUN: --whole-program-visibility-enabled-in-lto=true \
53+
; RUN: --validate-all-vtables-have-type-infos=true \
54+
; RUN: --all-vtables-have-type-infos=true \
55+
; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \
56+
; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \
57+
; RUN: -r=hybrid.o,_ZTV8DerivedN, \
58+
; RUN: -r=hybrid.o,_ZTI8DerivedN, \
59+
; RUN: -r=hybrid.o,_ZTS8DerivedN,p \
60+
; RUN: -r=hybrid.o,_ZTI7Derived,p \
61+
; RUN: -r=hybrid.o,_ZN8DerivedN5printEv, \
62+
; RUN: -r=hybrid.o,_ZTV8DerivedN,p \
63+
; RUN: -r=hybrid.o,_ZTI8DerivedN,p \
64+
; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK
65+
66+
67+
; RUN: llvm-lto2 run hybrid.o -save-temps -pass-remarks=. \
68+
; RUN: -thinlto-threads=1 \
69+
; RUN: -o hybrid-tmp \
70+
; RUN: --whole-program-visibility-enabled-in-lto=true \
71+
; RUN: --validate-all-vtables-have-type-infos=true \
72+
; RUN: --all-vtables-have-type-infos=true \
73+
; RUN: -r=hybrid.o,_ZN4Base8dispatchEv,px \
74+
; RUN: -r=hybrid.o,_ZN8DerivedN5printEv,px \
75+
; RUN: -r=hybrid.o,_ZTV8DerivedN, \
76+
; RUN: -r=hybrid.o,_ZTI8DerivedN, \
77+
; RUN: -r=hybrid.o,_ZTS8DerivedN,p \
78+
; RUN: -r=hybrid.o,_ZTI7Derived, \
79+
; RUN: -r=hybrid.o,_ZN8DerivedN5printEv, \
80+
; RUN: -r=hybrid.o,_ZTV8DerivedN,p \
81+
; RUN: -r=hybrid.o,_ZTI8DerivedN,p \
82+
; RUN: 2>&1 | FileCheck --allow-empty %s --implicit-check-not='single-impl: devirtualized a call to'
83+
84+
85+
; Repeat the above tests for WPD in regular LTO.
86+
; RUN: opt -module-summary -o regular.o %s
87+
88+
; RUN: llvm-lto2 run regular.o -save-temps -pass-remarks=. \
89+
; RUN: -thinlto-threads=1 \
90+
; RUN: -o regular-temp \
91+
; RUN: --whole-program-visibility-enabled-in-lto=true \
92+
; RUN: --validate-all-vtables-have-type-infos=true \
93+
; RUN: --all-vtables-have-type-infos=true \
94+
; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \
95+
; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \
96+
; RUN: -r=regular.o,_ZTS8DerivedN,p \
97+
; RUN: -r=regular.o,_ZTI7Derived,p \
98+
; RUN: -r=regular.o,_ZTV8DerivedN,p \
99+
; RUN: -r=regular.o,_ZTI8DerivedN,p \
100+
; RUN: 2>&1 | FileCheck --allow-empty %s --check-prefix=REMARK
101+
102+
; RUN: llvm-lto2 run regular.o -save-temps -pass-remarks=. \
103+
; RUN: -thinlto-threads=1 \
104+
; RUN: -o regular-temp \
105+
; RUN: --whole-program-visibility-enabled-in-lto=true \
106+
; RUN: --validate-all-vtables-have-type-infos=true \
107+
; RUN: --all-vtables-have-type-infos=true \
108+
; RUN: -r=regular.o,_ZN4Base8dispatchEv,px \
109+
; RUN: -r=regular.o,_ZN8DerivedN5printEv,px \
110+
; RUN: -r=regular.o,_ZTS8DerivedN,p \
111+
; RUN: -r=regular.o,_ZTI7Derived, \
112+
; RUN: -r=regular.o,_ZTV8DerivedN,p \
113+
; RUN: -r=regular.o,_ZTI8DerivedN,p \
114+
; RUN: 2>&1 | FileCheck --allow-empty %s --implicit-check-not='single-impl: devirtualized a call to'
115+
116+
; REMARK: single-impl: devirtualized a call to _ZN8DerivedN5printEv
117+
118+
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
119+
target triple = "x86_64-unknown-linux-gnu"
120+
121+
@_ZTV8DerivedN = linkonce_odr hidden constant { [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI8DerivedN, ptr @_ZN8DerivedN5printEv] }, !type !0, !type !1, !type !2, !type !3, !vcall_visibility !4
122+
@_ZTI8DerivedN = linkonce_odr hidden constant { ptr, ptr, ptr } { ptr null, ptr @_ZTS8DerivedN, ptr @_ZTI7Derived }
123+
@_ZTS8DerivedN = linkonce_odr hidden constant [10 x i8] c"8DerivedN\00", align 1
124+
@_ZTI7Derived = external constant ptr
125+
126+
; Whole program devirtualization will ignore summaries that are not live.
127+
; Mark '_ZTV8DerivedN' as used so it remains live.
128+
@llvm.used = appending global [1 x ptr] [ptr @_ZTV8DerivedN], section "llvm.metadata"
129+
130+
define hidden void @_ZN4Base8dispatchEv(ptr %this) {
131+
entry:
132+
%this.addr = alloca ptr
133+
store ptr %this, ptr %this.addr
134+
%this1 = load ptr, ptr %this.addr
135+
%vtable = load ptr, ptr %this1
136+
%0 = call i1 @llvm.type.test(ptr %vtable, metadata !"_ZTS7Derived")
137+
call void @llvm.assume(i1 %0)
138+
%vfn = getelementptr inbounds ptr, ptr %vtable, i64 0
139+
%1 = load ptr, ptr %vfn
140+
call void %1(ptr %this1)
141+
ret void
142+
}
143+
144+
define linkonce_odr hidden void @_ZN8DerivedN5printEv(ptr %this) #0 {
145+
entry:
146+
ret void
147+
}
148+
149+
attributes #0 = { noinline optnone }
150+
151+
declare i1 @llvm.type.test(ptr, metadata)
152+
declare void @llvm.assume(i1)
153+
154+
!0 = !{i64 16, !"_ZTS7Derived"}
155+
!1 = !{i64 16, !"_ZTSM7DerivedFvvE.virtual"}
156+
!2 = !{i64 16, !"_ZTS8DerivedN"}
157+
!3 = !{i64 16, !"_ZTSM8DerivedNFvvE.virtual"}
158+
!4 = !{i64 0}

llvm/tools/llvm-lto2/llvm-lto2.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,18 @@ static cl::opt<bool> EnableFreestanding(
187187
cl::desc("Enable Freestanding (disable builtins / TLI) during LTO"),
188188
cl::Hidden);
189189

190+
static cl::opt<bool> WholeProgramVisibilityEnabledInLTO(
191+
"whole-program-visibility-enabled-in-lto",
192+
cl::desc("Enable whole program visibility during LTO"), cl::Hidden);
193+
194+
static cl::opt<bool> ValidateAllVtablesHaveTypeInfos(
195+
"validate-all-vtables-have-type-infos",
196+
cl::desc("Validate that all vtables have type infos in LTO"), cl::Hidden);
197+
198+
static cl::opt<bool>
199+
AllVtablesHaveTypeInfos("all-vtables-have-type-infos", cl::Hidden,
200+
cl::desc("All vtables have type infos"));
201+
190202
extern cl::opt<cl::boolOrDefault> LoadBitcodeIntoNewDbgInfoFormat;
191203
extern cl::opt<cl::boolOrDefault> PreserveInputDbgFormat;
192204

@@ -332,6 +344,13 @@ static int run(int argc, char **argv) {
332344
Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
333345
Conf.PTO.SLPVectorization = Conf.OptLevel > 1;
334346

347+
if (WholeProgramVisibilityEnabledInLTO.getNumOccurrences() > 0)
348+
Conf.HasWholeProgramVisibility = WholeProgramVisibilityEnabledInLTO;
349+
if (ValidateAllVtablesHaveTypeInfos.getNumOccurrences() > 0)
350+
Conf.ValidateAllVtablesHaveTypeInfos = ValidateAllVtablesHaveTypeInfos;
351+
if (AllVtablesHaveTypeInfos.getNumOccurrences() > 0)
352+
Conf.AllVtablesHaveTypeInfos = AllVtablesHaveTypeInfos;
353+
335354
ThinBackend Backend;
336355
if (ThinLTODistributedIndexes)
337356
Backend = createWriteIndexesThinBackend(llvm::hardware_concurrency(Threads),

0 commit comments

Comments
 (0)