Skip to content

Commit eb7c47e

Browse files
committed
Resolve review comments
Change-Id: I3f6210cd7a088ae7186594d62b2d4f056fc5e732
1 parent 54296ce commit eb7c47e

File tree

5 files changed

+162
-64
lines changed

5 files changed

+162
-64
lines changed

clang/test/CodeGenCXX/devirt-single-impl.cpp

Lines changed: 0 additions & 56 deletions
This file was deleted.

clang/test/CodeGenCXX/type-metadata.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414
// RUN: %clang_cc1 -O2 -flto -flto-unit -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=ITANIUM-OPT --check-prefix=ITANIUM-OPT-LAYOUT %s
1515
// 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=MS-TYPEMETADATA --check-prefix=TT-MS %s
1616

17+
// Test for the whole-program-vtables feature in nonlto mode:
18+
// RUN: %clang_cc1 -triple x86_64-unknown-linux -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=VTABLE-OPT --check-prefix=TT-ITANIUM-DEFAULT-NOLTO %s
19+
1720
// Tests for cfi + whole-program-vtables:
1821
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-unknown-linux -fvisibility=hidden -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=ITANIUM-HIDDEN --check-prefix=ITANIUM-COMMON-MD --check-prefix=TC-ITANIUM --check-prefix=ITANIUM-NO-RV-MD %s
1922
// RUN: %clang_cc1 -flto -flto-unit -triple x86_64-pc-windows-msvc -fsanitize=cfi-vcall -fsanitize-trap=cfi-vcall -fwhole-program-vtables -emit-llvm -o - %s | FileCheck --check-prefix=CFI --check-prefix=CFI-VT --check-prefix=MS --check-prefix=MS-TYPEMETADATA --check-prefix=TC-MS %s
@@ -178,6 +181,7 @@ void af(A *a) {
178181
// TT-ITANIUM-HIDDEN: [[P:%[^ ]*]] = call i1 @llvm.type.test(ptr [[VT:%[^ ]*]], metadata !"_ZTS1A")
179182
// TT-ITANIUM-DEFAULT: [[P:%[^ ]*]] = call i1 @llvm.public.type.test(ptr [[VT:%[^ ]*]], metadata !"_ZTS1A")
180183
// TT-MS: [[P:%[^ ]*]] = call i1 @llvm.type.test(ptr [[VT:%[^ ]*]], metadata !"?AUA@@")
184+
// TT-ITANIUM-DEFAULT-NOLTO: [[P:%[^ ]*]] = call i1 @llvm.public.type.test(ptr [[VT:%[^ ]*]], metadata !"_ZTS1A")
181185
// TC-ITANIUM: [[PAIR:%[^ ]*]] = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata !"_ZTS1A")
182186
// TC-ITANIUM-RV: [[PAIR:%[^ ]*]] = call { ptr, i1 } @llvm.type.checked.load.relative(ptr {{%[^ ]*}}, i32 0, metadata !"_ZTS1A")
183187
// TC-MS: [[PAIR:%[^ ]*]] = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata !"?AUA@@")
@@ -212,6 +216,7 @@ void df1(D *d) {
212216
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
213217
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
214218
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata !"?AUA@@")
219+
// TT-ITANIUM-DEFAULT-NOLTO: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
215220
// TC-ITANIUM: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata ![[DTYPE:[0-9]+]])
216221
// TC-ITANIUM-RV: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load.relative(ptr {{%[^ ]*}}, i32 0, metadata ![[DTYPE:[0-9]+]])
217222
// TC-MS: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata !"?AUA@@")
@@ -224,6 +229,7 @@ void dg1(D *d) {
224229
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata !"_ZTS1B")
225230
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.public.type.test(ptr {{%[^ ]*}}, metadata !"_ZTS1B")
226231
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata !"?AUB@@")
232+
// TT-ITANIUM-DEFAULT-NOLTO: {{%[^ ]*}} = call i1 @llvm.public.type.test(ptr {{%[^ ]*}}, metadata !"_ZTS1B")
227233
// TC-ITANIUM: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 8, metadata !"_ZTS1B")
228234
// TC-ITANIUM-RV: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load.relative(ptr {{%[^ ]*}}, i32 4, metadata !"_ZTS1B")
229235
// TC-MS: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata !"?AUB@@")
@@ -236,6 +242,7 @@ void dh1(D *d) {
236242
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE]])
237243
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE]])
238244
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE:[0-9]+]])
245+
// TT-ITANIUM-DEFAULT-NOLTO: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata ![[DTYPE]])
239246
// TC-ITANIUM: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 16, metadata ![[DTYPE]])
240247
// TC-ITANIUM-RV: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load.relative(ptr {{%[^ ]*}}, i32 8, metadata ![[DTYPE]])
241248
// TC-MS: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 8, metadata ![[DTYPE:[0-9]+]])
@@ -297,6 +304,7 @@ void f(D *d) {
297304
// TT-ITANIUM-HIDDEN: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
298305
// TT-ITANIUM-DEFAULT: {{%[^ ]*}} = call i1 @llvm.public.type.test(ptr {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
299306
// TT-MS: {{%[^ ]*}} = call i1 @llvm.type.test(ptr {{%[^ ]*}}, metadata !"?AUA@test2@@")
307+
// TT-ITANIUM-DEFAULT-NOLTO: {{%[^ ]*}} = call i1 @llvm.public.type.test(ptr {{%[^ ]*}}, metadata !"_ZTSN5test21DE")
300308
// TC-ITANIUM: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 8, metadata !"_ZTSN5test21DE")
301309
// TC-ITANIUM-RV: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load.relative(ptr {{%[^ ]*}}, i32 4, metadata !"_ZTSN5test21DE")
302310
// TC-MS: {{%[^ ]*}} = call { ptr, i1 } @llvm.type.checked.load(ptr {{%[^ ]*}}, i32 0, metadata !"?AUA@test2@@")

llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ static cl::opt<WPDCheckMode> DevirtCheckMode(
221221
clEnumValN(WPDCheckMode::Fallback, "fallback",
222222
"Fallback to indirect when incorrect")));
223223

224+
// This pass runs with/out lto mode and the default is lto.
225+
// For testing, provide a way to tell that we are running outside lto mode.
226+
static cl::opt<bool> TestNoLTOMode(
227+
"wholeprogramdevirt-nolto", cl::Hidden,
228+
cl::desc("Run whole program devirt outside LTO mode."),
229+
cl::init(false));
230+
231+
224232
namespace {
225233
struct PatternList {
226234
std::vector<GlobPattern> Patterns;
@@ -804,6 +812,9 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
804812
return FAM.getResult<DominatorTreeAnalysis>(F);
805813
};
806814
if (UseCommandLine) {
815+
if (TestNoLTOMode)
816+
// we are outside LTO mode. enable speculative devirtualization:
817+
DevirtCheckMode = WPDCheckMode::Fallback;
807818
if (!DevirtModule::runForTesting(M, AARGetter, OREGetter, LookupDomTree))
808819
return PreservedAnalyses::all();
809820
return PreservedAnalyses::none();
@@ -2475,18 +2486,16 @@ bool DevirtModule::run() {
24752486
.WPDRes[S.first.ByteOffset];
24762487
if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos,
24772488
S.first.ByteOffset, ExportSummary)) {
2478-
trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res);
2489+
bool SingleImplDevirt = trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second,
2490+
Res);
24792491
// In Speculative devirt mode, we skip virtual constant propagation
24802492
// and branch funneling to minimize the drawback if we got wrong
24812493
// speculation during devirtualization.
2482-
if (DevirtCheckMode != WPDCheckMode::Fallback) {
2483-
if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second,
2484-
Res)) {
2485-
DidVirtualConstProp |=
2486-
tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
2494+
if (!SingleImplDevirt && DevirtCheckMode != WPDCheckMode::Fallback) {
2495+
DidVirtualConstProp |=
2496+
tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
24872497

2488-
tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
2489-
}
2498+
tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
24902499
}
24912500

24922501
// Collect functions devirtualized at least for one call site for stats.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
; -stats requires asserts
2+
; REQUIRES: asserts
3+
4+
; Check that we can still devirtualize outside LTO mode
5+
; Check that we skip devirtualization for empty functions outside LTO mode
6+
7+
; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-nolto -pass-remarks=wholeprogramdevirt -stats %s 2>&1 | FileCheck %s
8+
9+
target datalayout = "e-p:64:64"
10+
target triple = "x86_64-unknown-linux-gnu"
11+
12+
; CHECK: remark: devirt-single.cc:30:32: single-impl: devirtualized a call to vf
13+
; CHECK: remark: devirt-single.cc:41:32: single-impl: devirtualized a call to vf
14+
; CHECK: remark: devirt-single.cc:51:32: single-impl: devirtualized a call to vf
15+
; CHECK: remark: devirt-single.cc:13:0: devirtualized vf
16+
; CHECK-NOT: devirtualized
17+
18+
@vt1 = constant [1 x ptr] [ptr @vf], !type !8
19+
@vt2 = constant [1 x ptr] [ptr @vf_empty], !type !12
20+
21+
define i1 @vf(ptr %this) #0 !dbg !7 {
22+
ret i1 true
23+
}
24+
25+
; This should NOT be devietualized because during non-lto empty functions
26+
; are skipped.
27+
define void @vf_empty(ptr %this) !dbg !11 {
28+
ret void
29+
}
30+
31+
; CHECK: define void @call
32+
define void @call(ptr %obj) #1 !dbg !5 {
33+
%vtable = load ptr, ptr %obj
34+
%p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid")
35+
call void @llvm.assume(i1 %p)
36+
%fptr = load ptr, ptr %vtable
37+
; CHECK: if.true.direct_targ:
38+
; CHECK: call i1 @vf(
39+
; CHECK: if.false.orig_indirect:
40+
; CHECK: call i1 %fptr(
41+
call i1 %fptr(ptr %obj), !dbg !6
42+
ret void
43+
}
44+
45+
46+
; CHECK: define void @call1
47+
define void @call1(ptr %obj) #1 !dbg !9 {
48+
%vtable = load ptr, ptr %obj
49+
%p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid1")
50+
call void @llvm.assume(i1 %p)
51+
%fptr = load ptr, ptr %vtable, align 8
52+
; CHECK: call i1 %fptr
53+
%1 = call i1 %fptr(ptr %obj), !dbg !10
54+
ret void
55+
}
56+
declare ptr @llvm.load.relative.i32(ptr, i32)
57+
58+
@vt3 = private unnamed_addr constant [1 x i32] [
59+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf to i64), i64 ptrtoint (ptr @vt3 to i64)) to i32)
60+
], align 4, !type !15
61+
62+
; CHECK: define void @call2
63+
define void @call2(ptr %obj) #1 !dbg !13 {
64+
%vtable = load ptr, ptr %obj
65+
%p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid2")
66+
call void @llvm.assume(i1 %p)
67+
%fptr = call ptr @llvm.load.relative.i32(ptr %vtable, i32 0)
68+
; CHECK: if.true.direct_targ:
69+
; CHECK: call i1 @vf(
70+
; CHECK: if.false.orig_indirect:
71+
; CHECK: call i1 %fptr(
72+
call i1 %fptr(ptr %obj), !dbg !14
73+
ret void
74+
}
75+
76+
@_ZTV1A.local = private unnamed_addr constant { [3 x i32] } { [3 x i32] [
77+
i32 0, ; offset to top
78+
i32 0, ; rtti
79+
i32 trunc (i64 sub (i64 ptrtoint (ptr dso_local_equivalent @vf to i64), i64 ptrtoint (ptr getelementptr inbounds ({ [3 x i32] }, ptr @_ZTV1A.local, i32 0, i32 0, i32 2) to i64)) to i32) ; vf_emptyunc offset
80+
] }, align 4, !type !18
81+
82+
; CHECK: define void @call3
83+
define void @call3(ptr %obj) #1 !dbg !16 {
84+
%vtable = load ptr, ptr %obj
85+
%p = call i1 @llvm.type.test(ptr %vtable, metadata !"typeid3")
86+
call void @llvm.assume(i1 %p)
87+
%fptr = call ptr @llvm.load.relative.i32(ptr %vtable, i32 8)
88+
; CHECK: if.true.direct_targ:
89+
; CHECK: call i1 @vf(
90+
; CHECK: if.false.orig_indirect:
91+
; CHECK: call i1 %fptr(
92+
call i1 %fptr(ptr %obj), !dbg !17
93+
ret void
94+
}
95+
96+
97+
declare i1 @llvm.type.test(ptr, metadata)
98+
declare void @llvm.assume(i1)
99+
100+
!llvm.dbg.cu = !{!0}
101+
!llvm.module.flags = !{!2, !3}
102+
!llvm.ident = !{!4}
103+
104+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 4.0.0 (trunk 278098)", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug)
105+
!1 = !DIFile(filename: "devirt-single.cc", directory: ".")
106+
!2 = !{i32 2, !"Dwarf Version", i32 4}
107+
!3 = !{i32 2, !"Debug Info Version", i32 3}
108+
!4 = !{!"clang version 4.0.0 (trunk 278098)"}
109+
!5 = distinct !DISubprogram(name: "call", linkageName: "_Z4callPv", scope: !1, file: !1, line: 29, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
110+
!6 = !DILocation(line: 30, column: 32, scope: !5)
111+
!7 = distinct !DISubprogram(name: "vf", linkageName: "_ZN3vt12vfEv", scope: !1, file: !1, line: 13, isLocal: false, isDefinition: true, scopeLine: 13, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
112+
!8 = !{i32 0, !"typeid"}
113+
114+
!9 = distinct !DISubprogram(name: "call1", linkageName: "_Z5call1Pv", scope: !1, file: !1, line: 31, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
115+
!10 = !DILocation(line: 35, column: 32, scope: !9)
116+
!11 = distinct !DISubprogram(name: "vf_empty", linkageName: "_ZN3vt18vf_emptyEv", scope: !1, file: !1, line: 23, isLocal: false, isDefinition: true, scopeLine: 23, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
117+
!12 = !{i32 0, !"typeid1"}
118+
119+
!13 = distinct !DISubprogram(name: "call2", linkageName: "_Z5call2Pv", scope: !1, file: !1, line: 40, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
120+
!14 = !DILocation(line: 41, column: 32, scope: !13)
121+
!15 = !{i32 0, !"typeid2"}
122+
123+
!16 = distinct !DISubprogram(name: "call3", linkageName: "_Z5call3Pv", scope: !1, file: !1, line: 50, isLocal: false, isDefinition: true, scopeLine: 9, flags: DIFlagPrototyped, isOptimized: false, unit: !0)
124+
!17 = !DILocation(line: 51, column: 32, scope: !16)
125+
!18 = !{i32 0, !"typeid3"}
126+
127+
128+
129+
; CHECK: 1 wholeprogramdevirt - Number of whole program devirtualization targets
130+
; CHECK: 3 wholeprogramdevirt - Number of single implementation devirtualizations

llvm/test/Transforms/WholeProgramDevirt/virtual-const-prop-check.ll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
; Check wildcard
1212
; RUN: opt -S -passes=wholeprogramdevirt -whole-program-visibility -pass-remarks=wholeprogramdevirt -wholeprogramdevirt-skip=vf?i1 %s 2>&1 | FileCheck %s --check-prefix=SKIP
1313

14+
; Check that no stats are reported when we enable devirtualization out of LTO mode.
15+
; RUN: opt -S -passes=wholeprogramdevirt -wholeprogramdevirt-nolto -stats %s 2>&1 | FileCheck %s --check-prefix=CHECK-WPD-NOLTO
16+
1417
target datalayout = "e-p:64:64"
1518
target triple = "x86_64-unknown-linux-gnu"
1619

@@ -225,3 +228,7 @@ declare ptr @llvm.load.relative.i32(ptr, i32)
225228
; CHECK: 2 wholeprogramdevirt - Number of unique return value optimizations
226229
; CHECK: 2 wholeprogramdevirt - Number of virtual constant propagations
227230
; CHECK: 2 wholeprogramdevirt - Number of 1 bit virtual constant propagations
231+
232+
; CHECK-WPD-NOLTO-NOT: 0 wholeprogramdevirt - Number of unique return value optimizations
233+
; CHECK-WPD-NOLTO-NOT: 0 wholeprogramdevirt - Number of virtual constant propagations
234+
; CHECK-WPD-NOLTO-NOT: 0 wholeprogramdevirt - Number of 1 bit virtual constant propagations

0 commit comments

Comments
 (0)