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