xref: /llvm-project/llvm/test/Transforms/LowerIFunc/lower-ifunc.ll (revision e7cd42f8e4da1beed52f401dcf87d22d36a2c81c)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals --include-generated-funcs
2; RUN: opt -S -passes=lower-ifunc < %s | FileCheck %s
3
4@initialized_with_ifunc = global ptr @ifunc_constant_initializer_user
5
6@ifunc_1 = ifunc void (), ptr @resolver1
7@ifunc_2 = ifunc void (), ptr @resolver1
8
9
10; Keep one with no users
11@ifunc_no_users = ifunc float (i64), ptr @resolver2
12
13
14@ifunc7 = ifunc float (i64), ptr @resolver3
15@ifunc_ptr_arg = ifunc void (ptr), ptr @resolver4
16
17@ifunc_nonvoid_0 = ifunc i32 (double), ptr @resolver5
18@ifunc_nonvoid_1 = ifunc i32 (double), ptr @resolver5
19@ifunc_constant_initializer_user = ifunc i32 (double), ptr @resolver5
20
21define ptr @resolver1() {
22  ret ptr inttoptr (i64 123 to ptr)
23}
24
25define ptr @resolver2() {
26  ret ptr inttoptr (i64 456 to ptr)
27}
28
29define ptr @resolver3() {
30  ret ptr inttoptr (i64 789 to ptr)
31}
32
33define ptr @resolver4() {
34  ret ptr inttoptr (i64 999 to ptr)
35}
36
37define ptr @resolver5() {
38  ret ptr inttoptr (i64 420 to ptr)
39}
40
41
42; Test call to  ifunc
43define void @call_ifunc(ptr %ptr) {
44  call void @ifunc_1()
45  call void @ifunc_2()
46  ret void
47}
48
49; Test value use of  ifunc
50define void @store_ifunc_2(ptr %ptr) {
51  store ptr @ifunc_2, ptr %ptr
52  store ptr %ptr, ptr @ifunc_2
53  ret void
54}
55
56declare void @other_func(ptr)
57
58; Check a call user, but not as the call operand
59define void @call_ifunc_is_argument(ptr %ptr) {
60  call void @other_func(ptr @ifunc_2)
61  ret void
62}
63
64; Check a call user calling the ifunc, and using the ifunc as an argument
65define void @call_ifunc_both_call_argument(ptr %ptr) {
66  call void @ifunc_ptr_arg(ptr @ifunc_ptr_arg)
67  ret void
68}
69
70define i32 @call_ifunc_nonvoid(double %arg) {
71  %ret = call i32 @ifunc_nonvoid_0(double %arg)
72  ret i32 %ret
73}
74
75; Use site is different than ifunc function type
76define float @call_different_type_ifunc_nonvoid(double %arg) {
77  %cast.arg = bitcast double %arg to i64
78  %ret = call float(i64) @ifunc_nonvoid_0(i64 %cast.arg)
79  ret float %ret
80}
81
82; FIXME: Should be able to expand this, but we miss the call
83; instruction in the constexpr cast.
84define i32 @call_addrspacecast_callee_type_ifunc_nonvoid(double %arg) {
85  %ret = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1)) (double %arg)
86  ret i32 %ret
87}
88
89define i32 @call_used_in_initializer(double %arg) {
90  %ret = call i32 @ifunc_constant_initializer_user(double %arg)
91  ret i32 %ret
92}
93;.
94; CHECK: @[[INITIALIZED_WITH_IFUNC:[a-zA-Z0-9_$"\\.-]+]] = global ptr @ifunc_constant_initializer_user
95; CHECK: @[[GLOB0:[0-9]+]] = internal global [8 x ptr] poison, align 8
96; CHECK: @[[LLVM_GLOBAL_CTORS:[a-zA-Z0-9_$"\\.-]+]] = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 10, ptr @[[GLOB1:[0-9]+]], ptr null }]
97; CHECK: @[[IFUNC_NONVOID_1:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5
98; CHECK: @[[IFUNC_CONSTANT_INITIALIZER_USER:[a-zA-Z0-9_$"\\.-]+]] = ifunc i32 (double), ptr @resolver5
99;.
100; CHECK-LABEL: define {{[^@]+}}@resolver1(
101; CHECK-NEXT:    ret ptr inttoptr (i64 123 to ptr)
102;
103;
104; CHECK-LABEL: define {{[^@]+}}@resolver2(
105; CHECK-NEXT:    ret ptr inttoptr (i64 456 to ptr)
106;
107;
108; CHECK-LABEL: define {{[^@]+}}@resolver3(
109; CHECK-NEXT:    ret ptr inttoptr (i64 789 to ptr)
110;
111;
112; CHECK-LABEL: define {{[^@]+}}@resolver4(
113; CHECK-NEXT:    ret ptr inttoptr (i64 999 to ptr)
114;
115;
116; CHECK-LABEL: define {{[^@]+}}@resolver5(
117; CHECK-NEXT:    ret ptr inttoptr (i64 420 to ptr)
118;
119;
120; CHECK-LABEL: define {{[^@]+}}@call_ifunc(
121; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr @[[GLOB0]], align 8
122; CHECK-NEXT:    call void [[TMP1]]()
123; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8
124; CHECK-NEXT:    call void [[TMP2]]()
125; CHECK-NEXT:    ret void
126;
127;
128; CHECK-LABEL: define {{[^@]+}}@store_ifunc_2(
129; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8
130; CHECK-NEXT:    store ptr [[TMP1]], ptr [[PTR:%.*]], align 8
131; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8
132; CHECK-NEXT:    store ptr [[PTR]], ptr [[TMP2]], align 8
133; CHECK-NEXT:    ret void
134;
135;
136; CHECK-LABEL: define {{[^@]+}}@call_ifunc_is_argument(
137; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8
138; CHECK-NEXT:    call void @other_func(ptr [[TMP1]])
139; CHECK-NEXT:    ret void
140;
141;
142; CHECK-LABEL: define {{[^@]+}}@call_ifunc_both_call_argument(
143; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8
144; CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8
145; CHECK-NEXT:    call void [[TMP1]](ptr [[TMP1]])
146; CHECK-NEXT:    ret void
147;
148;
149; CHECK-LABEL: define {{[^@]+}}@call_ifunc_nonvoid(
150; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8
151; CHECK-NEXT:    [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]])
152; CHECK-NEXT:    ret i32 [[RET]]
153;
154;
155; CHECK-LABEL: define {{[^@]+}}@call_different_type_ifunc_nonvoid(
156; CHECK-NEXT:    [[CAST_ARG:%.*]] = bitcast double [[ARG:%.*]] to i64
157; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8
158; CHECK-NEXT:    [[RET:%.*]] = call float [[TMP1]](i64 [[CAST_ARG]])
159; CHECK-NEXT:    ret float [[RET]]
160;
161;
162; CHECK-LABEL: define {{[^@]+}}@call_addrspacecast_callee_type_ifunc_nonvoid(
163; CHECK-NEXT:    [[RET:%.*]] = call addrspace(1) i32 addrspacecast (ptr @ifunc_nonvoid_1 to ptr addrspace(1))(double [[ARG:%.*]])
164; CHECK-NEXT:    ret i32 [[RET]]
165;
166;
167; CHECK-LABEL: define {{[^@]+}}@call_used_in_initializer(
168; CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8
169; CHECK-NEXT:    [[RET:%.*]] = call i32 [[TMP1]](double [[ARG:%.*]])
170; CHECK-NEXT:    ret i32 [[RET]]
171;
172;
173; CHECK-LABEL: define {{[^@]+}}@1(
174; CHECK-NEXT:    [[TMP1:%.*]] = call ptr @resolver1()
175; CHECK-NEXT:    store ptr [[TMP1]], ptr @[[GLOB0]], align 8
176; CHECK-NEXT:    [[TMP2:%.*]] = call ptr @resolver1()
177; CHECK-NEXT:    store ptr [[TMP2]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 1), align 8
178; CHECK-NEXT:    [[TMP3:%.*]] = call ptr @resolver2()
179; CHECK-NEXT:    store ptr [[TMP3]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 2), align 8
180; CHECK-NEXT:    [[TMP4:%.*]] = call ptr @resolver3()
181; CHECK-NEXT:    store ptr [[TMP4]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 3), align 8
182; CHECK-NEXT:    [[TMP5:%.*]] = call ptr @resolver4()
183; CHECK-NEXT:    store ptr [[TMP5]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 4), align 8
184; CHECK-NEXT:    [[TMP6:%.*]] = call ptr @resolver5()
185; CHECK-NEXT:    store ptr [[TMP6]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 5), align 8
186; CHECK-NEXT:    [[TMP7:%.*]] = call ptr @resolver5()
187; CHECK-NEXT:    store ptr [[TMP7]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 6), align 8
188; CHECK-NEXT:    [[TMP8:%.*]] = call ptr @resolver5()
189; CHECK-NEXT:    store ptr [[TMP8]], ptr getelementptr inbounds ([8 x ptr], ptr @[[GLOB0]], i32 0, i32 7), align 8
190; CHECK-NEXT:    ret void
191;
192