xref: /llvm-project/llvm/test/CodeGen/LoongArch/calling-conv-common.ll (revision 1897bf61f0bc85c8637997d0f2aa7d94d375d787)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc --mtriple=loongarch64 --target-abi=lp64s < %s | FileCheck %s
3; RUN: llc --mtriple=loongarch64 --mattr=+d --target-abi=lp64d < %s | FileCheck %s
4
5;; This file contains tests that should have identical output for all ABIs, i.e.
6;; where no arguments are passed via floating point registers.
7
8;; Check that on LA64, i128 is passed in a pair of GPRs.
9define i64 @callee_i128_in_regs(i64 %a, i128 %b) nounwind {
10; CHECK-LABEL: callee_i128_in_regs:
11; CHECK:       # %bb.0:
12; CHECK-NEXT:    add.d $a0, $a0, $a1
13; CHECK-NEXT:    ret
14  %b_trunc = trunc i128 %b to i64
15  %1 = add i64 %a, %b_trunc
16  ret i64 %1
17}
18
19define i64 @caller_i128_in_regs() nounwind {
20; CHECK-LABEL: caller_i128_in_regs:
21; CHECK:       # %bb.0:
22; CHECK-NEXT:    addi.d $sp, $sp, -16
23; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
24; CHECK-NEXT:    ori $a0, $zero, 1
25; CHECK-NEXT:    ori $a1, $zero, 2
26; CHECK-NEXT:    move $a2, $zero
27; CHECK-NEXT:    bl %plt(callee_i128_in_regs)
28; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
29; CHECK-NEXT:    addi.d $sp, $sp, 16
30; CHECK-NEXT:    ret
31  %1 = call i64 @callee_i128_in_regs(i64 1, i128 2)
32  ret i64 %1
33}
34
35;; Check that the stack is used once the GPRs are exhausted.
36define i64 @callee_many_scalars(i8 %a, i16 %b, i32 %c, i64 %d, i128 %e, i64 %f, i128 %g, i64 %h) nounwind {
37; CHECK-LABEL: callee_many_scalars:
38; CHECK:       # %bb.0:
39; CHECK-NEXT:    ld.d $t0, $sp, 8
40; CHECK-NEXT:    ld.d $t1, $sp, 0
41; CHECK-NEXT:    andi $a0, $a0, 255
42; CHECK-NEXT:    bstrpick.d $a1, $a1, 15, 0
43; CHECK-NEXT:    bstrpick.d $a2, $a2, 31, 0
44; CHECK-NEXT:    add.d $a0, $a0, $a1
45; CHECK-NEXT:    add.d $a0, $a0, $a2
46; CHECK-NEXT:    add.d $a0, $a0, $a3
47; CHECK-NEXT:    xor $a1, $a5, $t1
48; CHECK-NEXT:    xor $a2, $a4, $a7
49; CHECK-NEXT:    or $a1, $a2, $a1
50; CHECK-NEXT:    sltui $a1, $a1, 1
51; CHECK-NEXT:    add.d $a0, $a1, $a0
52; CHECK-NEXT:    add.d $a0, $a0, $a6
53; CHECK-NEXT:    add.d $a0, $a0, $t0
54; CHECK-NEXT:    ret
55  %a_ext = zext i8 %a to i64
56  %b_ext = zext i16 %b to i64
57  %c_ext = zext i32 %c to i64
58  %1 = add i64 %a_ext, %b_ext
59  %2 = add i64 %1, %c_ext
60  %3 = add i64 %2, %d
61  %4 = icmp eq i128 %e, %g
62  %5 = zext i1 %4 to i64
63  %6 = add i64 %5, %3
64  %7 = add i64 %6, %f
65  %8 = add i64 %7, %h
66  ret i64 %8
67}
68
69define i64 @caller_many_scalars() nounwind {
70; CHECK-LABEL: caller_many_scalars:
71; CHECK:       # %bb.0:
72; CHECK-NEXT:    addi.d $sp, $sp, -32
73; CHECK-NEXT:    st.d $ra, $sp, 24 # 8-byte Folded Spill
74; CHECK-NEXT:    ori $a0, $zero, 8
75; CHECK-NEXT:    st.d $a0, $sp, 8
76; CHECK-NEXT:    ori $a0, $zero, 1
77; CHECK-NEXT:    ori $a1, $zero, 2
78; CHECK-NEXT:    ori $a2, $zero, 3
79; CHECK-NEXT:    ori $a3, $zero, 4
80; CHECK-NEXT:    ori $a4, $zero, 5
81; CHECK-NEXT:    ori $a6, $zero, 6
82; CHECK-NEXT:    ori $a7, $zero, 7
83; CHECK-NEXT:    st.d $zero, $sp, 0
84; CHECK-NEXT:    move $a5, $zero
85; CHECK-NEXT:    bl %plt(callee_many_scalars)
86; CHECK-NEXT:    ld.d $ra, $sp, 24 # 8-byte Folded Reload
87; CHECK-NEXT:    addi.d $sp, $sp, 32
88; CHECK-NEXT:    ret
89  %1 = call i64 @callee_many_scalars(i8 1, i16 2, i32 3, i64 4, i128 5, i64 6, i128 7, i64 8)
90  ret i64 %1
91}
92
93;; Check that i256 is passed indirectly.
94
95define i64 @callee_large_scalars(i256 %a, i256 %b) nounwind {
96; CHECK-LABEL: callee_large_scalars:
97; CHECK:       # %bb.0:
98; CHECK-NEXT:    ld.d $a2, $a1, 0
99; CHECK-NEXT:    ld.d $a3, $a0, 0
100; CHECK-NEXT:    ld.d $a4, $a1, 8
101; CHECK-NEXT:    ld.d $a5, $a1, 24
102; CHECK-NEXT:    ld.d $a6, $a0, 24
103; CHECK-NEXT:    ld.d $a7, $a0, 8
104; CHECK-NEXT:    ld.d $a1, $a1, 16
105; CHECK-NEXT:    ld.d $a0, $a0, 16
106; CHECK-NEXT:    xor $a5, $a6, $a5
107; CHECK-NEXT:    xor $a4, $a7, $a4
108; CHECK-NEXT:    or $a4, $a4, $a5
109; CHECK-NEXT:    xor $a0, $a0, $a1
110; CHECK-NEXT:    xor $a1, $a3, $a2
111; CHECK-NEXT:    or $a0, $a1, $a0
112; CHECK-NEXT:    or $a0, $a0, $a4
113; CHECK-NEXT:    sltui $a0, $a0, 1
114; CHECK-NEXT:    ret
115  %1 = icmp eq i256 %a, %b
116  %2 = zext i1 %1 to i64
117  ret i64 %2
118}
119
120define i64 @caller_large_scalars() nounwind {
121; CHECK-LABEL: caller_large_scalars:
122; CHECK:       # %bb.0:
123; CHECK-NEXT:    addi.d $sp, $sp, -80
124; CHECK-NEXT:    st.d $ra, $sp, 72 # 8-byte Folded Spill
125; CHECK-NEXT:    st.d $zero, $sp, 24
126; CHECK-NEXT:    vrepli.b $vr0, 0
127; CHECK-NEXT:    vst $vr0, $sp, 8
128; CHECK-NEXT:    ori $a0, $zero, 2
129; CHECK-NEXT:    st.d $a0, $sp, 0
130; CHECK-NEXT:    st.d $zero, $sp, 56
131; CHECK-NEXT:    vst $vr0, $sp, 40
132; CHECK-NEXT:    ori $a2, $zero, 1
133; CHECK-NEXT:    addi.d $a0, $sp, 32
134; CHECK-NEXT:    addi.d $a1, $sp, 0
135; CHECK-NEXT:    st.d $a2, $sp, 32
136; CHECK-NEXT:    bl %plt(callee_large_scalars)
137; CHECK-NEXT:    ld.d $ra, $sp, 72 # 8-byte Folded Reload
138; CHECK-NEXT:    addi.d $sp, $sp, 80
139; CHECK-NEXT:    ret
140  %1 = call i64 @callee_large_scalars(i256 1, i256 2)
141  ret i64 %1
142}
143
144;; Check that arguments larger than 2*GRLen are handled correctly when their
145;; address is passed on the stack rather than in memory.
146
147;; Must keep define on a single line due to an update_llc_test_checks.py limitation
148define i64 @callee_large_scalars_exhausted_regs(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f, i64 %g, i256 %h, i64 %i, i256 %j) nounwind {
149; CHECK-LABEL: callee_large_scalars_exhausted_regs:
150; CHECK:       # %bb.0:
151; CHECK-NEXT:    ld.d $a0, $sp, 8
152; CHECK-NEXT:    ld.d $a1, $a0, 0
153; CHECK-NEXT:    ld.d $a2, $a7, 0
154; CHECK-NEXT:    ld.d $a3, $a0, 8
155; CHECK-NEXT:    ld.d $a4, $a0, 24
156; CHECK-NEXT:    ld.d $a5, $a7, 24
157; CHECK-NEXT:    ld.d $a6, $a7, 8
158; CHECK-NEXT:    ld.d $a0, $a0, 16
159; CHECK-NEXT:    ld.d $a7, $a7, 16
160; CHECK-NEXT:    xor $a4, $a5, $a4
161; CHECK-NEXT:    xor $a3, $a6, $a3
162; CHECK-NEXT:    or $a3, $a3, $a4
163; CHECK-NEXT:    xor $a0, $a7, $a0
164; CHECK-NEXT:    xor $a1, $a2, $a1
165; CHECK-NEXT:    or $a0, $a1, $a0
166; CHECK-NEXT:    or $a0, $a0, $a3
167; CHECK-NEXT:    sltui $a0, $a0, 1
168; CHECK-NEXT:    ret
169  %1 = icmp eq i256 %h, %j
170  %2 = zext i1 %1 to i64
171  ret i64 %2
172}
173
174define i64 @caller_large_scalars_exhausted_regs() nounwind {
175; CHECK-LABEL: caller_large_scalars_exhausted_regs:
176; CHECK:       # %bb.0:
177; CHECK-NEXT:    addi.d $sp, $sp, -96
178; CHECK-NEXT:    st.d $ra, $sp, 88 # 8-byte Folded Spill
179; CHECK-NEXT:    addi.d $a0, $sp, 16
180; CHECK-NEXT:    st.d $a0, $sp, 8
181; CHECK-NEXT:    ori $a0, $zero, 9
182; CHECK-NEXT:    st.d $a0, $sp, 0
183; CHECK-NEXT:    st.d $zero, $sp, 40
184; CHECK-NEXT:    vrepli.b $vr0, 0
185; CHECK-NEXT:    vst $vr0, $sp, 24
186; CHECK-NEXT:    ori $a0, $zero, 10
187; CHECK-NEXT:    st.d $a0, $sp, 16
188; CHECK-NEXT:    st.d $zero, $sp, 72
189; CHECK-NEXT:    ori $a0, $zero, 8
190; CHECK-NEXT:    st.d $a0, $sp, 48
191; CHECK-NEXT:    ori $a0, $zero, 1
192; CHECK-NEXT:    ori $a1, $zero, 2
193; CHECK-NEXT:    ori $a2, $zero, 3
194; CHECK-NEXT:    ori $a3, $zero, 4
195; CHECK-NEXT:    ori $a4, $zero, 5
196; CHECK-NEXT:    ori $a5, $zero, 6
197; CHECK-NEXT:    ori $a6, $zero, 7
198; CHECK-NEXT:    addi.d $a7, $sp, 48
199; CHECK-NEXT:    vst $vr0, $sp, 56
200; CHECK-NEXT:    bl %plt(callee_large_scalars_exhausted_regs)
201; CHECK-NEXT:    ld.d $ra, $sp, 88 # 8-byte Folded Reload
202; CHECK-NEXT:    addi.d $sp, $sp, 96
203; CHECK-NEXT:    ret
204  %1 = call i64 @callee_large_scalars_exhausted_regs(
205      i64 1, i64 2, i64 3, i64 4, i64 5, i64 6, i64 7, i256 8, i64 9,
206      i256 10)
207  ret i64 %1
208}
209
210;; Check large struct arguments, which are passed byval
211
212%struct.large = type { i64, i64, i64, i64 }
213
214define i64 @callee_large_struct(ptr byval(%struct.large) align 8 %a) nounwind {
215; CHECK-LABEL: callee_large_struct:
216; CHECK:       # %bb.0:
217; CHECK-NEXT:    ld.d $a1, $a0, 0
218; CHECK-NEXT:    ld.d $a0, $a0, 24
219; CHECK-NEXT:    add.d $a0, $a1, $a0
220; CHECK-NEXT:    ret
221  %1 = getelementptr inbounds %struct.large, ptr %a, i64 0, i32 0
222  %2 = getelementptr inbounds %struct.large, ptr %a, i64 0, i32 3
223  %3 = load i64, ptr %1
224  %4 = load i64, ptr %2
225  %5 = add i64 %3, %4
226  ret i64 %5
227}
228
229define i64 @caller_large_struct() nounwind {
230; CHECK-LABEL: caller_large_struct:
231; CHECK:       # %bb.0:
232; CHECK-NEXT:    addi.d $sp, $sp, -80
233; CHECK-NEXT:    st.d $ra, $sp, 72 # 8-byte Folded Spill
234; CHECK-NEXT:    ori $a0, $zero, 1
235; CHECK-NEXT:    st.d $a0, $sp, 40
236; CHECK-NEXT:    ori $a1, $zero, 2
237; CHECK-NEXT:    st.d $a1, $sp, 48
238; CHECK-NEXT:    ori $a2, $zero, 3
239; CHECK-NEXT:    st.d $a2, $sp, 56
240; CHECK-NEXT:    ori $a3, $zero, 4
241; CHECK-NEXT:    st.d $a3, $sp, 64
242; CHECK-NEXT:    st.d $a0, $sp, 8
243; CHECK-NEXT:    st.d $a1, $sp, 16
244; CHECK-NEXT:    st.d $a2, $sp, 24
245; CHECK-NEXT:    st.d $a3, $sp, 32
246; CHECK-NEXT:    addi.d $a0, $sp, 8
247; CHECK-NEXT:    bl %plt(callee_large_struct)
248; CHECK-NEXT:    ld.d $ra, $sp, 72 # 8-byte Folded Reload
249; CHECK-NEXT:    addi.d $sp, $sp, 80
250; CHECK-NEXT:    ret
251  %ls = alloca %struct.large, align 8
252  %a = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 0
253  store i64 1, ptr %a
254  %b = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 1
255  store i64 2, ptr %b
256  %c = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 2
257  store i64 3, ptr %c
258  %d = getelementptr inbounds %struct.large, ptr %ls, i64 0, i32 3
259  store i64 4, ptr %d
260  %1 = call i64 @callee_large_struct(ptr byval(%struct.large) align 8 %ls)
261  ret i64 %1
262}
263
264;; Check return scalar which size is 2*GRLen.
265
266define i128 @callee_small_scalar_ret() nounwind {
267; CHECK-LABEL: callee_small_scalar_ret:
268; CHECK:       # %bb.0:
269; CHECK-NEXT:    addi.w $a0, $zero, -1
270; CHECK-NEXT:    move $a1, $a0
271; CHECK-NEXT:    ret
272  ret i128 -1
273}
274
275define i64 @caller_small_scalar_ret() nounwind {
276; CHECK-LABEL: caller_small_scalar_ret:
277; CHECK:       # %bb.0:
278; CHECK-NEXT:    addi.d $sp, $sp, -16
279; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
280; CHECK-NEXT:    bl %plt(callee_small_scalar_ret)
281; CHECK-NEXT:    addi.w $a2, $zero, -2
282; CHECK-NEXT:    xor $a0, $a0, $a2
283; CHECK-NEXT:    orn $a0, $a0, $a1
284; CHECK-NEXT:    sltui $a0, $a0, 1
285; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
286; CHECK-NEXT:    addi.d $sp, $sp, 16
287; CHECK-NEXT:    ret
288  %1 = call i128 @callee_small_scalar_ret()
289  %2 = icmp eq i128 -2, %1
290  %3 = zext i1 %2 to i64
291  ret i64 %3
292}
293
294;; Check return struct which size is 2*GRLen.
295
296%struct.small = type { i64, ptr }
297
298define %struct.small @callee_small_struct_ret() nounwind {
299; CHECK-LABEL: callee_small_struct_ret:
300; CHECK:       # %bb.0:
301; CHECK-NEXT:    ori $a0, $zero, 1
302; CHECK-NEXT:    move $a1, $zero
303; CHECK-NEXT:    ret
304  ret %struct.small { i64 1, ptr null }
305}
306
307define i64 @caller_small_struct_ret() nounwind {
308; CHECK-LABEL: caller_small_struct_ret:
309; CHECK:       # %bb.0:
310; CHECK-NEXT:    addi.d $sp, $sp, -16
311; CHECK-NEXT:    st.d $ra, $sp, 8 # 8-byte Folded Spill
312; CHECK-NEXT:    bl %plt(callee_small_struct_ret)
313; CHECK-NEXT:    add.d $a0, $a0, $a1
314; CHECK-NEXT:    ld.d $ra, $sp, 8 # 8-byte Folded Reload
315; CHECK-NEXT:    addi.d $sp, $sp, 16
316; CHECK-NEXT:    ret
317  %1 = call %struct.small @callee_small_struct_ret()
318  %2 = extractvalue %struct.small %1, 0
319  %3 = extractvalue %struct.small %1, 1
320  %4 = ptrtoint ptr %3 to i64
321  %5 = add i64 %2, %4
322  ret i64 %5
323}
324
325;; Check return scalar which size is more than 2*GRLen.
326
327define i256 @callee_large_scalar_ret() nounwind {
328; CHECK-LABEL: callee_large_scalar_ret:
329; CHECK:       # %bb.0:
330; CHECK-NEXT:    addi.w $a1, $zero, -1
331; CHECK-NEXT:    st.d $a1, $a0, 24
332; CHECK-NEXT:    st.d $a1, $a0, 16
333; CHECK-NEXT:    st.d $a1, $a0, 8
334; CHECK-NEXT:    lu12i.w $a1, -30141
335; CHECK-NEXT:    ori $a1, $a1, 747
336; CHECK-NEXT:    st.d $a1, $a0, 0
337; CHECK-NEXT:    ret
338  ret i256 -123456789
339}
340
341define void @caller_large_scalar_ret() nounwind {
342; CHECK-LABEL: caller_large_scalar_ret:
343; CHECK:       # %bb.0:
344; CHECK-NEXT:    addi.d $sp, $sp, -48
345; CHECK-NEXT:    st.d $ra, $sp, 40 # 8-byte Folded Spill
346; CHECK-NEXT:    addi.d $a0, $sp, 0
347; CHECK-NEXT:    bl %plt(callee_large_scalar_ret)
348; CHECK-NEXT:    ld.d $ra, $sp, 40 # 8-byte Folded Reload
349; CHECK-NEXT:    addi.d $sp, $sp, 48
350; CHECK-NEXT:    ret
351  %1 = call i256 @callee_large_scalar_ret()
352  ret void
353}
354
355;; Check return struct which size is more than 2*GRLen.
356
357define void @callee_large_struct_ret(ptr noalias sret(%struct.large) %agg.result) nounwind {
358; CHECK-LABEL: callee_large_struct_ret:
359; CHECK:       # %bb.0:
360; CHECK-NEXT:    ori $a1, $zero, 1
361; CHECK-NEXT:    st.d $a1, $a0, 0
362; CHECK-NEXT:    ori $a1, $zero, 2
363; CHECK-NEXT:    st.d $a1, $a0, 8
364; CHECK-NEXT:    ori $a1, $zero, 3
365; CHECK-NEXT:    st.d $a1, $a0, 16
366; CHECK-NEXT:    ori $a1, $zero, 4
367; CHECK-NEXT:    st.d $a1, $a0, 24
368; CHECK-NEXT:    ret
369  %a = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 0
370  store i64 1, ptr %a, align 4
371  %b = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 1
372  store i64 2, ptr %b, align 4
373  %c = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 2
374  store i64 3, ptr %c, align 4
375  %d = getelementptr inbounds %struct.large, ptr %agg.result, i64 0, i32 3
376  store i64 4, ptr %d, align 4
377  ret void
378}
379
380define i64 @caller_large_struct_ret() nounwind {
381; CHECK-LABEL: caller_large_struct_ret:
382; CHECK:       # %bb.0:
383; CHECK-NEXT:    addi.d $sp, $sp, -48
384; CHECK-NEXT:    st.d $ra, $sp, 40 # 8-byte Folded Spill
385; CHECK-NEXT:    addi.d $a0, $sp, 8
386; CHECK-NEXT:    bl %plt(callee_large_struct_ret)
387; CHECK-NEXT:    ld.d $a0, $sp, 8
388; CHECK-NEXT:    ld.d $a1, $sp, 32
389; CHECK-NEXT:    add.d $a0, $a0, $a1
390; CHECK-NEXT:    ld.d $ra, $sp, 40 # 8-byte Folded Reload
391; CHECK-NEXT:    addi.d $sp, $sp, 48
392; CHECK-NEXT:    ret
393  %1 = alloca %struct.large
394  call void @callee_large_struct_ret(ptr sret(%struct.large) %1)
395  %2 = getelementptr inbounds %struct.large, ptr %1, i64 0, i32 0
396  %3 = load i64, ptr %2
397  %4 = getelementptr inbounds %struct.large, ptr %1, i64 0, i32 3
398  %5 = load i64, ptr %4
399  %6 = add i64 %3, %5
400  ret i64 %6
401}
402