xref: /llvm-project/llvm/test/CodeGen/SPARC/tailcall.ll (revision ff9af4c43ad71eeba2cabe99609cfaa0fd54c1d0)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc < %s -mtriple=sparc -verify-machineinstrs | FileCheck %s --check-prefix=V8
3; RUN: llc < %s -mtriple=sparcv9 -verify-machineinstrs | FileCheck %s --check-prefix=V9
4
5define i32 @simple_leaf(i32 %i) #0 {
6; V8-LABEL: simple_leaf:
7; V8:       ! %bb.0: ! %entry
8; V8-NEXT:    mov %o7, %g1
9; V8-NEXT:    call foo
10; V8-NEXT:    mov %g1, %o7
11;
12; V9-LABEL: simple_leaf:
13; V9:       ! %bb.0: ! %entry
14; V9-NEXT:    mov %o7, %g1
15; V9-NEXT:    call foo
16; V9-NEXT:    mov %g1, %o7
17entry:
18  %call = tail call i32 @foo(i32 %i)
19  ret i32 %call
20}
21
22define i32 @simple_standard(i32 %i) #1 {
23; V8-LABEL: simple_standard:
24; V8:       ! %bb.0: ! %entry
25; V8-NEXT:    save %sp, -96, %sp
26; V8-NEXT:    call foo
27; V8-NEXT:    restore
28;
29; V9-LABEL: simple_standard:
30; V9:       ! %bb.0: ! %entry
31; V9-NEXT:    save %sp, -128, %sp
32; V9-NEXT:    call foo
33; V9-NEXT:    restore
34entry:
35  %call = tail call i32 @foo(i32 %i)
36  ret i32 %call
37}
38
39define i32 @extra_arg_leaf(i32 %i) #0 {
40; V8-LABEL: extra_arg_leaf:
41; V8:       ! %bb.0: ! %entry
42; V8-NEXT:    mov 12, %o1
43; V8-NEXT:    mov %o7, %g1
44; V8-NEXT:    call foo2
45; V8-NEXT:    mov %g1, %o7
46;
47; V9-LABEL: extra_arg_leaf:
48; V9:       ! %bb.0: ! %entry
49; V9-NEXT:    mov 12, %o1
50; V9-NEXT:    mov %o7, %g1
51; V9-NEXT:    call foo2
52; V9-NEXT:    mov %g1, %o7
53entry:
54  %call = tail call i32 @foo2(i32 %i, i32 12)
55  ret i32 %call
56}
57
58define i32 @extra_arg_standard(i32 %i) #1 {
59; V8-LABEL: extra_arg_standard:
60; V8:       ! %bb.0: ! %entry
61; V8-NEXT:    save %sp, -96, %sp
62; V8-NEXT:    call foo2
63; V8-NEXT:    restore %g0, 12, %o1
64;
65; V9-LABEL: extra_arg_standard:
66; V9:       ! %bb.0: ! %entry
67; V9-NEXT:    save %sp, -128, %sp
68; V9-NEXT:    call foo2
69; V9-NEXT:    restore %g0, 12, %o1
70entry:
71  %call = tail call i32 @foo2(i32 %i, i32 12)
72  ret i32 %call
73}
74
75; Perform tail call optimization for external symbol.
76
77define void @caller_extern(ptr %src) optsize #0 {
78; V8-LABEL: caller_extern:
79; V8:       ! %bb.0: ! %entry
80; V8-NEXT:    sethi %hi(dest), %o1
81; V8-NEXT:    add %o1, %lo(dest), %o1
82; V8-NEXT:    mov 7, %o2
83; V8-NEXT:    mov %o0, %o3
84; V8-NEXT:    mov %o1, %o0
85; V8-NEXT:    mov %o3, %o1
86; V8-NEXT:    mov %o7, %g1
87; V8-NEXT:    call memcpy
88; V8-NEXT:    mov %g1, %o7
89;
90; V9-LABEL: caller_extern:
91; V9:       ! %bb.0: ! %entry
92; V9-NEXT:    sethi %h44(dest), %o1
93; V9-NEXT:    add %o1, %m44(dest), %o1
94; V9-NEXT:    sllx %o1, 12, %o1
95; V9-NEXT:    add %o1, %l44(dest), %o1
96; V9-NEXT:    mov 7, %o2
97; V9-NEXT:    mov %o0, %o3
98; V9-NEXT:    mov %o1, %o0
99; V9-NEXT:    mov %o3, %o1
100; V9-NEXT:    mov %o7, %g1
101; V9-NEXT:    call memcpy
102; V9-NEXT:    mov %g1, %o7
103entry:
104  tail call void @llvm.memcpy.p0.p0.i32(
105    ptr @dest,
106    ptr %src, i32 7, i1 false)
107  ret void
108}
109
110; Perform tail call optimization for function pointer.
111
112define i32 @func_ptr_test(ptr nocapture %func_ptr) #0 {
113; V8-LABEL: func_ptr_test:
114; V8:       ! %bb.0: ! %entry
115; V8-NEXT:    jmp %o0
116; V8-NEXT:    nop
117;
118; V9-LABEL: func_ptr_test:
119; V9:       ! %bb.0: ! %entry
120; V9-NEXT:    jmp %o0
121; V9-NEXT:    nop
122entry:
123  %call = tail call i32 %func_ptr() #1
124  ret i32 %call
125}
126
127define i32 @func_ptr_test2(ptr nocapture %func_ptr,
128; V8-LABEL: func_ptr_test2:
129; V8:       ! %bb.0: ! %entry
130; V8-NEXT:    save %sp, -96, %sp
131; V8-NEXT:    mov 10, %i3
132; V8-NEXT:    mov %i0, %i4
133; V8-NEXT:    mov %i1, %i0
134; V8-NEXT:    jmp %i4
135; V8-NEXT:    restore %g0, %i3, %o1
136;
137; V9-LABEL: func_ptr_test2:
138; V9:       ! %bb.0: ! %entry
139; V9-NEXT:    save %sp, -128, %sp
140; V9-NEXT:    mov 10, %i3
141; V9-NEXT:    mov %i0, %i4
142; V9-NEXT:    mov %i1, %i0
143; V9-NEXT:    jmp %i4
144; V9-NEXT:    restore %g0, %i3, %o1
145                           i32 %r, i32 %q) #1 {
146entry:
147  %call = tail call i32 %func_ptr(i32 %r, i32 10, i32 %q) #1
148  ret i32 %call
149}
150
151
152; Do not tail call optimize if stack is used to pass parameters.
153
154define i32 @caller_args() #0 {
155; V8-LABEL: caller_args:
156; V8:       ! %bb.0: ! %entry
157; V8-NEXT:    save %sp, -104, %sp
158; V8-NEXT:    mov 6, %i0
159; V8-NEXT:    mov 1, %o1
160; V8-NEXT:    mov 2, %o2
161; V8-NEXT:    mov 3, %o3
162; V8-NEXT:    mov 4, %o4
163; V8-NEXT:    mov 5, %o5
164; V8-NEXT:    st %i0, [%sp+92]
165; V8-NEXT:    call foo7
166; V8-NEXT:    mov %g0, %o0
167; V8-NEXT:    ret
168; V8-NEXT:    restore %g0, %o0, %o0
169;
170; V9-LABEL: caller_args:
171; V9:       ! %bb.0: ! %entry
172; V9-NEXT:    save %sp, -192, %sp
173; V9-NEXT:    mov 6, %i0
174; V9-NEXT:    mov 1, %o1
175; V9-NEXT:    mov 2, %o2
176; V9-NEXT:    mov 3, %o3
177; V9-NEXT:    mov 4, %o4
178; V9-NEXT:    mov 5, %o5
179; V9-NEXT:    stx %i0, [%sp+2223]
180; V9-NEXT:    call foo7
181; V9-NEXT:    mov %g0, %o0
182; V9-NEXT:    ret
183; V9-NEXT:    restore %g0, %o0, %o0
184entry:
185  %r = tail call i32 @foo7(i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6)
186  ret i32 %r
187}
188
189; Byval parameters hand the function a pointer directly into the stack area
190; we want to reuse during a tail call. Do not tail call optimize functions with
191; byval parameters.
192
193define i32 @caller_byval() #0 {
194; V8-LABEL: caller_byval:
195; V8:       ! %bb.0: ! %entry
196; V8-NEXT:    save %sp, -104, %sp
197; V8-NEXT:    ld [%fp+-4], %i0
198; V8-NEXT:    st %i0, [%fp+-8]
199; V8-NEXT:    call callee_byval
200; V8-NEXT:    add %fp, -8, %o0
201; V8-NEXT:    ret
202; V8-NEXT:    restore %g0, %o0, %o0
203;
204; V9-LABEL: caller_byval:
205; V9:       ! %bb.0: ! %entry
206; V9-NEXT:    save %sp, -192, %sp
207; V9-NEXT:    call callee_byval
208; V9-NEXT:    add %fp, 2039, %o0
209; V9-NEXT:    ret
210; V9-NEXT:    restore %g0, %o0, %o0
211entry:
212  %a = alloca ptr
213  %r = tail call i32 @callee_byval(ptr byval(ptr) %a)
214  ret i32 %r
215}
216
217; Perform tail call optimization for sret function.
218
219define void @sret_test(ptr noalias sret(%struct.a) %agg.result) #0 {
220; V8-LABEL: sret_test:
221; V8:       ! %bb.0: ! %entry
222; V8-NEXT:    mov %o7, %g1
223; V8-NEXT:    call sret_func
224; V8-NEXT:    mov %g1, %o7
225;
226; V9-LABEL: sret_test:
227; V9:       ! %bb.0: ! %entry
228; V9-NEXT:    mov %o7, %g1
229; V9-NEXT:    call sret_func
230; V9-NEXT:    mov %g1, %o7
231entry:
232  tail call void @sret_func(ptr sret(%struct.a) %agg.result)
233  ret void
234}
235
236; Do not tail call if either caller or callee returns
237; a struct and the other does not. Returning a large
238; struct will generate a memcpy as the tail function.
239
240define void @ret_large_struct(ptr noalias sret(%struct.big) %agg.result) #0 {
241; V8-LABEL: ret_large_struct:
242; V8:       ! %bb.0: ! %entry
243; V8-NEXT:    save %sp, -96, %sp
244; V8-NEXT:    ld [%fp+64], %i0
245; V8-NEXT:    sethi %hi(bigstruct), %i1
246; V8-NEXT:    add %i1, %lo(bigstruct), %o1
247; V8-NEXT:    mov 400, %o2
248; V8-NEXT:    call memcpy
249; V8-NEXT:    mov %i0, %o0
250; V8-NEXT:    jmp %i7+12
251; V8-NEXT:    restore
252;
253; V9-LABEL: ret_large_struct:
254; V9:       ! %bb.0: ! %entry
255; V9-NEXT:    save %sp, -176, %sp
256; V9-NEXT:    sethi %h44(bigstruct), %i1
257; V9-NEXT:    add %i1, %m44(bigstruct), %i1
258; V9-NEXT:    sllx %i1, 12, %i1
259; V9-NEXT:    add %i1, %l44(bigstruct), %o1
260; V9-NEXT:    mov 400, %o2
261; V9-NEXT:    call memcpy
262; V9-NEXT:    mov %i0, %o0
263; V9-NEXT:    ret
264; V9-NEXT:    restore
265entry:
266  %0 = bitcast ptr %agg.result to ptr
267  tail call void @llvm.memcpy.p0.p0.i32(ptr align 4 %0, ptr align 4 @bigstruct, i32 400, i1 false)
268  ret void
269}
270
271; Test register + immediate pattern.
272
273define void @addri_test(i32 %ptr) #0 {
274; V8-LABEL: addri_test:
275; V8:       ! %bb.0: ! %entry
276; V8-NEXT:    jmp %o0+4
277; V8-NEXT:    nop
278;
279; V9-LABEL: addri_test:
280; V9:       ! %bb.0: ! %entry
281; V9-NEXT:    add %o0, 4, %o0
282; V9-NEXT:    srl %o0, 0, %o0
283; V9-NEXT:    jmp %o0
284; V9-NEXT:    nop
285entry:
286  %add = add nsw i32 %ptr, 4
287  %0 = inttoptr i32 %add to ptr
288  tail call void %0() #1
289  ret void
290}
291
292%struct.a = type { i32, i32 }
293@dest = global [2 x i8] zeroinitializer
294
295%struct.big = type { [100 x i32] }
296@bigstruct = global %struct.big zeroinitializer
297
298declare void @llvm.memcpy.p0.p0.i32(ptr, ptr, i32, i1)
299declare void @sret_func(ptr sret(%struct.a))
300declare i32 @callee_byval(ptr byval(ptr) %a)
301declare i32 @foo(i32)
302declare i32 @foo2(i32, i32)
303declare i32 @foo7(i32, i32, i32, i32, i32, i32, i32)
304
305attributes #0 = { nounwind "disable-tail-calls"="false"
306                  "frame-pointer"="none" }
307attributes #1 = { nounwind "disable-tail-calls"="false"
308                  "frame-pointer"="all" }
309