xref: /llvm-project/llvm/test/tools/llvm-reduce/remove-ifunc.ll (revision bc3e492323f34eb92f49897d22468aef2d831d5a)
1; RUN: llvm-reduce --abort-on-invalid-reduction --delta-passes=ifuncs --test FileCheck --test-arg --check-prefixes=CHECK-INTERESTINGNESS --test-arg %s --test-arg --input-file %s -o %t
2; RUN: FileCheck --check-prefixes=CHECK-FINAL --input-file=%t %s
3
4
5; CHECK-FINAL: @initialized_with_ifunc = global ptr @ifunc_constant_initializer_user
6@initialized_with_ifunc = global ptr @ifunc_constant_initializer_user
7
8
9; CHECK-FINAL: [[TABLE:@[0-9]+]] = internal global [[[TABLE_SIZE:[0-9]+]] x ptr] poison
10; CHECK-FINAL: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @1, ptr null }]
11
12
13; CHECK-INTERESTINGNESS: @ifunc_kept1 = ifunc
14
15; CHECK-FINAL: @ifunc_kept1 = ifunc void (), ptr @resolver1
16@ifunc_kept1 = ifunc void (), ptr @resolver1
17@ifunc_removed2 = ifunc void (), ptr @resolver1
18
19; CHECK-INTERESTINGNESS: @ifunc_kept3 =
20; CHECK-FINAL: @ifunc_kept3 = ifunc i32 (double), ptr @resolver2
21@ifunc_kept3 = ifunc i32 (double), ptr @resolver2
22
23
24; Remove one with no users
25@ifunc4_removed = ifunc float (i64), ptr @resolver2
26
27; Keep one with no users
28; CHECK-INTERESTINGNESS: @ifunc5_kept = ifunc
29@ifunc5_kept = ifunc float (i64), ptr @resolver2
30
31
32; Make sure the hidden is preserved
33; CHECK-INTERESTINGNESS: @ifunc_kept_hidden =
34; CHECK-FINAL: @ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3
35@ifunc_kept_hidden = hidden ifunc i32 (double), ptr @resolver3
36@ifunc7 = ifunc float (i64), ptr @resolver3
37
38@ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4
39
40
41; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept0 = ifunc
42@ifunc_nonvoid_kept0 = ifunc i32 (double), ptr @resolver5
43@ifunc_nonvoid_removed0 = ifunc i32 (double), ptr @resolver5
44
45; CHECK-INTERESTINGNESS: @ifunc_nonvoid_kept1 = ifunc
46@ifunc_nonvoid_kept1 = ifunc i32 (double), ptr @resolver5
47@ifunc_nonvoid_removed1 = ifunc i32 (double), ptr @resolver5
48
49; CHECK-FINAL: @ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5
50@ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5
51
52
53
54define ptr @resolver1() {
55  ret ptr inttoptr (i64 123 to ptr)
56}
57
58define ptr @resolver2() {
59  ret ptr inttoptr (i64 456 to ptr)
60}
61
62define ptr @resolver3() {
63  ret ptr inttoptr (i64 789 to ptr)
64}
65
66define ptr @resolver4() {
67  ret ptr inttoptr (i64 999 to ptr)
68}
69
70define ptr @resolver5() {
71  ret ptr inttoptr (i64 420 to ptr)
72}
73
74define void @call_ifunc_kept1() {
75  ; CHECK-FINAL-LABEL: define void @call_ifunc_kept1() {
76  ; CHECK-FINAL-NEXT: call void @ifunc_kept1()
77  ; CHECK-FINAL-NEXT: ret void
78  call void @ifunc_kept1()
79  ret void
80}
81
82; Test call to removed ifunc
83define void @call_ifunc_removed(ptr %ptr) {
84  ; CHECK-FINAL-LABEL: define void @call_ifunc_removed(ptr %ptr)
85  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr @0, align 8
86  ; CHECK-FINAL-NEXT:  call void %1()
87  ; CHECK-FINAL-NEXT:  ret void
88  call void @ifunc_removed2()
89  ret void
90}
91
92; Test value use of removed ifunc
93define void @store_ifunc_removed2(ptr %ptr) {
94  ; CHECK-FINAL-LABEL: define void @store_ifunc_removed2(ptr %ptr) {
95  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8
96  ; CHECK-FINAL-NEXT: store ptr %1, ptr %ptr, align 8
97  ; CHECK-FINAL-NEXT: %2 = load ptr, ptr @0, align 8
98  ; CHECK-FINAL-NEXT: store ptr %ptr, ptr %2, align 8
99  ; CHECK-FINAL-NEXT: ret void
100  store ptr @ifunc_removed2, ptr %ptr
101  store ptr %ptr, ptr @ifunc_removed2
102  ret void
103}
104
105declare void @other_func(ptr)
106
107; Check a call user, but not as the call operand
108define void @call_ifunc_removed_is_argument(ptr %ptr) {
109  ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_is_argument(ptr %ptr) {
110  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr [[TABLE]], align 8
111  ; CHECK-FINAL-NEXT: call void @other_func(ptr %1)
112  ; CHECK-FINAL-NEXT: ret void
113  call void @other_func(ptr @ifunc_removed2)
114  ret void
115}
116
117; Check a call user calling the ifunc, and using the ifunc as an argument
118define void @call_ifunc_removed_both_call_argument(ptr %ptr) {
119  ; CHECK-FINAL-LABEL: define void @call_ifunc_removed_both_call_argument(
120  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8
121  ; CHECK-FINAL-NEXT: %2 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 3), align 8
122  ; CHECK-FINAL-NEXT: call void %1(ptr %1)
123  ; CHECK-FINAL-NEXT: ret void
124  call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg)
125  ret void
126}
127
128define i32 @call_ifunc_nonvoid(double %arg) {
129  ; CHECK-FINAL-LABEL: define i32 @call_ifunc_nonvoid(double %arg) {
130  ; CHECK-FINAL-NEXT: %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg)
131  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8
132  ; CHECK-FINAL-NEXT: %ret1 = call i32 %1(double %arg)
133  ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1
134  ; CHECK-FINAL-NEXT: ret i32 %add
135  %ret0 = call i32 @ifunc_nonvoid_kept0(double %arg)
136  %ret1 = call i32 @ifunc_nonvoid_removed0(double %arg)
137  %add = add i32 %ret0, %ret1
138  ret i32 %add
139}
140
141; Use site is different than ifunc function type
142define float @call_different_type_ifunc_nonvoid(double %arg) {
143  ; CHECK-FINAL-LABEL: define float @call_different_type_ifunc_nonvoid(double %arg) {
144  ; CHECK-FINAL-NEXT: %cast.arg = bitcast double %arg to i64
145  ; CHECK-FINAL-NEXT: %ret0 = call float @ifunc_nonvoid_kept0(i64 %cast.arg)
146  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([[[TABLE_SIZE]] x ptr], ptr [[TABLE]], i32 0, i32 4), align 8
147  ; CHECK-FINAL-NEXT: %ret1 = call float %1(i64 %cast.arg)
148  ; CHECK-FINAL-NEXT: %fadd = fadd float %ret0, %ret1
149  ; CHECK-FINAL-NEXT: ret float %fadd
150  %cast.arg = bitcast double %arg to i64
151  %ret0 = call float(i64) @ifunc_nonvoid_kept0(i64 %cast.arg)
152  %ret1 = call float(i64) @ifunc_nonvoid_removed0(i64 %cast.arg)
153  %fadd = fadd float %ret0, %ret1
154  ret float %fadd
155}
156
157; FIXME: Should be able to expand this, but we miss the call
158; instruction in the constexpr cast.
159define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) {
160  ; CHECK-FINAL-LABEL: define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) {
161  ; CHECK-FINAL-NEXT: %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1))(double %arg)
162  ; CHECK-FINAL-NEXT: %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1))(double %arg)
163  ; CHECK-FINAL-NEXT: %add = add i32 %ret0, %ret1
164  ; CHECK-FINAL-NEXT: ret i32 %add
165  %ret0 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_kept1 to ptr addrspace(1)) (double %arg)
166  %ret1 = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_removed1 to ptr addrspace(1)) (double %arg)
167  %add = add i32 %ret0, %ret1
168  ret i32 %add
169}
170
171define i32 @call_used_in_initializer(double %arg) {
172  ; CHECK-FINAL-LABEL: define i32 @call_used_in_initializer(double %arg) {
173  ; CHECK-FINAL-NEXT: %1 = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8
174  ; CHECK-FINAL-NEXT: %ret = call i32 %1(double %arg)
175  ; CHECK-FINAL-NEXT: ret i32 %ret
176  %ret = call i32 @ifunc_constant_initializer_user(double %arg)
177  ret i32 %ret
178}
179
180; CHECK-FINAL-LABEL: define internal void @1() {
181; CHECK-FINAL-NEXT: %1 = call ptr @resolver1()
182; CHECK-FINAL-NEXT: store ptr %1, ptr @0, align 8
183; CHECK-FINAL-NEXT: %2 = call ptr @resolver2()
184; CHECK-FINAL-NEXT: store ptr %2, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 1), align 8
185; CHECK-FINAL-NEXT: %3 = call ptr @resolver3()
186; CHECK-FINAL-NEXT: store ptr %3, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 2), align 8
187; CHECK-FINAL-NEXT: %4 = call ptr @resolver4()
188; CHECK-FINAL-NEXT: store ptr %4, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 3), align 8
189; CHECK-FINAL-NEXT: %5 = call ptr @resolver5()
190; CHECK-FINAL-NEXT: store ptr %5, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 4), align 8
191; CHECK-FINAL-NEXT: %6 = call ptr @resolver5()
192; CHECK-FINAL-NEXT: store ptr %6, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 5), align 8
193; CHECK-FINAL-NEXT: %7 = call ptr @resolver5()
194; CHECK-FINAL-NEXT: store ptr %7, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 6), align 8
195; CHECK-FINAL-NEXT: %8 = call ptr @resolver5()
196; CHECK-FINAL-NEXT: store ptr %8, ptr getelementptr inbounds ([8 x ptr], ptr @0, i32 0, i32 7), align 8
197; CHECK-FINAL-NEXT: ret void
198; CHECK-FINAL-NEXT: }
199