xref: /llvm-project/llvm/test/CodeGen/Thumb/callee_save.ll (revision 2d9c6e699a09d1363e435e6692508dd290984a00)
1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s
3
4declare ptr @llvm.returnaddress(i32)
5
6; We don't allocate high registers, so any function not using inline asm will
7; only need to save the low registers.
8define void @low_regs_only() {
9; CHECK-LABEL: low_regs_only:
10; CHECK:       @ %bb.0: @ %entry
11; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
12; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
13; CHECK-NEXT:    @APP
14; CHECK-NEXT:    @NO_APP
15; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
16entry:
17  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"()
18  ret void
19}
20
21; One high reg clobbered, but no low regs, args or returns. We can use an
22; argument/return register to help save/restore it.
23define void @one_high() {
24; CHECK-LABEL: one_high:
25; CHECK:       @ %bb.0: @ %entry
26; CHECK-NEXT:    mov r3, r8
27; CHECK-NEXT:    .save {r8}
28; CHECK-NEXT:    push {r3}
29; CHECK-NEXT:    @APP
30; CHECK-NEXT:    @NO_APP
31; CHECK-NEXT:    pop {r0}
32; CHECK-NEXT:    mov r8, r0
33; CHECK-NEXT:    bx lr
34entry:
35  tail call void asm sideeffect "", "~{r8}"()
36  ret void
37}
38
39; 4 high regs clobbered, but still no low regs, args or returns. We can use all
40; 4 arg/return regs for the save/restore.
41define void @four_high() {
42; CHECK-LABEL: four_high:
43; CHECK:       @ %bb.0: @ %entry
44; CHECK-NEXT:    mov r3, r11
45; CHECK-NEXT:    mov r2, r10
46; CHECK-NEXT:    mov r1, r9
47; CHECK-NEXT:    mov r0, r8
48; CHECK-NEXT:    .save {r8, r9, r10, r11}
49; CHECK-NEXT:    push {r0, r1, r2, r3}
50; CHECK-NEXT:    @APP
51; CHECK-NEXT:    @NO_APP
52; CHECK-NEXT:    pop {r0, r1, r2, r3}
53; CHECK-NEXT:    mov r8, r0
54; CHECK-NEXT:    mov r9, r1
55; CHECK-NEXT:    mov r10, r2
56; CHECK-NEXT:    mov r11, r3
57; CHECK-NEXT:    bx lr
58entry:
59  tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()
60  ret void
61}
62
63; One high and one low register clobbered. lr also gets pushed to simplify the
64; return, and r7 to keep the stack aligned. Here, we could use r0-r3, r4, r7 or
65; lr to save/restore r8.
66define void @one_high_one_low() {
67; CHECK-LABEL: one_high_one_low:
68; CHECK:       @ %bb.0: @ %entry
69; CHECK-NEXT:    .save {r4, r7, lr}
70; CHECK-NEXT:    push {r4, r7, lr}
71; CHECK-NEXT:    mov lr, r8
72; CHECK-NEXT:    .save {r8}
73; CHECK-NEXT:    push {lr}
74; CHECK-NEXT:    @APP
75; CHECK-NEXT:    @NO_APP
76; CHECK-NEXT:    pop {r0}
77; CHECK-NEXT:    mov r8, r0
78; CHECK-NEXT:    pop {r4, r7, pc}
79entry:
80  tail call void asm sideeffect "", "~{r4},~{r8}"()
81  ret void
82}
83
84; All callee-saved registers clobbered, r4-r7 and lr are not live after the
85; first push so can be used for pushing the high registers.
86define void @four_high_four_low() {
87; CHECK-LABEL: four_high_four_low:
88; CHECK:       @ %bb.0: @ %entry
89; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
90; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
91; CHECK-NEXT:    mov lr, r11
92; CHECK-NEXT:    mov r7, r10
93; CHECK-NEXT:    mov r6, r9
94; CHECK-NEXT:    mov r5, r8
95; CHECK-NEXT:    .save {r8, r9, r10, r11}
96; CHECK-NEXT:    push {r5, r6, r7, lr}
97; CHECK-NEXT:    @APP
98; CHECK-NEXT:    @NO_APP
99; CHECK-NEXT:    pop {r0, r1, r2, r3}
100; CHECK-NEXT:    mov r8, r0
101; CHECK-NEXT:    mov r9, r1
102; CHECK-NEXT:    mov r10, r2
103; CHECK-NEXT:    mov r11, r3
104; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
105entry:
106  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
107  ret void
108}
109
110
111; All callee-saved registers clobbered, and frame pointer is requested. r7 now
112; cannot be used while saving/restoring the high regs.
113define void @four_high_four_low_frame_ptr() "frame-pointer"="all" {
114; CHECK-LABEL: four_high_four_low_frame_ptr:
115; CHECK:       @ %bb.0: @ %entry
116; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
117; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
118; CHECK-NEXT:    .setfp r7, sp, #12
119; CHECK-NEXT:    add r7, sp, #12
120; CHECK-NEXT:    mov lr, r11
121; CHECK-NEXT:    mov r6, r10
122; CHECK-NEXT:    mov r5, r9
123; CHECK-NEXT:    mov r4, r8
124; CHECK-NEXT:    .save {r8, r9, r10, r11}
125; CHECK-NEXT:    push {r4, r5, r6, lr}
126; CHECK-NEXT:    @APP
127; CHECK-NEXT:    @NO_APP
128; CHECK-NEXT:    pop {r0, r1, r2, r3}
129; CHECK-NEXT:    mov r8, r0
130; CHECK-NEXT:    mov r9, r1
131; CHECK-NEXT:    mov r10, r2
132; CHECK-NEXT:    mov r11, r3
133; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
134entry:
135  tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
136  ret void
137}
138
139; All callee-saved registers clobbered, frame pointer is requested and
140; llvm.returnaddress used. r7 and lr now cannot be used while saving/restoring
141; the high regs.
142define void @four_high_four_low_frame_ptr_ret_addr() "frame-pointer"="all" {
143; CHECK-LABEL: four_high_four_low_frame_ptr_ret_addr:
144; CHECK:       @ %bb.0: @ %entry
145; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
146; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
147; CHECK-NEXT:    .setfp r7, sp, #12
148; CHECK-NEXT:    add r7, sp, #12
149; CHECK-NEXT:    mov r6, r11
150; CHECK-NEXT:    mov r5, r10
151; CHECK-NEXT:    mov r4, r9
152; CHECK-NEXT:    mov r3, r8
153; CHECK-NEXT:    .save {r8, r9, r10, r11}
154; CHECK-NEXT:    push {r3, r4, r5, r6}
155; CHECK-NEXT:    mov r0, lr
156; CHECK-NEXT:    @APP
157; CHECK-NEXT:    @NO_APP
158; CHECK-NEXT:    pop {r0, r1, r2, r3}
159; CHECK-NEXT:    mov r8, r0
160; CHECK-NEXT:    mov r9, r1
161; CHECK-NEXT:    mov r10, r2
162; CHECK-NEXT:    mov r11, r3
163; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
164entry:
165  %a = tail call ptr @llvm.returnaddress(i32 0)
166  tail call void asm sideeffect "", "r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(ptr %a)
167  ret void
168}
169
170; 4 high regs clobbered, all 4 argument registers used. We push an extra 4 low
171; registers, so that we can use them for saving the high regs.
172define void @four_high_four_arg(i32 %a, i32 %b, i32 %c, i32 %d) {
173; CHECK-LABEL: four_high_four_arg:
174; CHECK:       @ %bb.0: @ %entry
175; CHECK-NEXT:    .save {r5, r6, r7, lr}
176; CHECK-NEXT:    push {r5, r6, r7, lr}
177; CHECK-NEXT:    mov lr, r11
178; CHECK-NEXT:    mov r7, r10
179; CHECK-NEXT:    mov r6, r9
180; CHECK-NEXT:    mov r5, r8
181; CHECK-NEXT:    .save {r8, r9, r10, r11}
182; CHECK-NEXT:    push {r5, r6, r7, lr}
183; CHECK-NEXT:    @APP
184; CHECK-NEXT:    @NO_APP
185; CHECK-NEXT:    pop {r0, r1, r2, r3}
186; CHECK-NEXT:    mov r8, r0
187; CHECK-NEXT:    mov r9, r1
188; CHECK-NEXT:    mov r10, r2
189; CHECK-NEXT:    mov r11, r3
190; CHECK-NEXT:    pop {r5, r6, r7, pc}
191entry:
192  tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d)
193  ret void
194}
195
196; 4 high regs clobbered, all 4 return registers used. We push an extra 4 low
197; registers, so that we can use them for restoring the high regs.
198define <4 x i32> @four_high_four_return() {
199; CHECK-LABEL: four_high_four_return:
200; CHECK:       @ %bb.0: @ %entry
201; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
202; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
203; CHECK-NEXT:    mov lr, r11
204; CHECK-NEXT:    mov r7, r10
205; CHECK-NEXT:    mov r6, r9
206; CHECK-NEXT:    mov r5, r8
207; CHECK-NEXT:    .save {r8, r9, r10, r11}
208; CHECK-NEXT:    push {r5, r6, r7, lr}
209; CHECK-NEXT:    @APP
210; CHECK-NEXT:    @NO_APP
211; CHECK-NEXT:    movs r0, #1
212; CHECK-NEXT:    movs r1, #2
213; CHECK-NEXT:    movs r2, #3
214; CHECK-NEXT:    movs r3, #4
215; CHECK-NEXT:    pop {r4, r5, r6, r7}
216; CHECK-NEXT:    mov r8, r4
217; CHECK-NEXT:    mov r9, r5
218; CHECK-NEXT:    mov r10, r6
219; CHECK-NEXT:    mov r11, r7
220; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
221entry:
222  tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()
223  %vecinit = insertelement <4 x i32> undef, i32 1, i32 0
224  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
225  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
226  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
227  ret <4 x i32> %vecinit13
228}
229
230; 4 high regs clobbered, all args & returns used, frame pointer requested and
231; llvm.returnaddress called. This leaves us with 3 low registers available (r4,
232; r5, r6), with which to save 4 high registers, so we have to use two pushes
233; and pops.
234define <4 x i32> @all_of_the_above(i32 %a, i32 %b, i32 %c, i32 %d) "frame-pointer"="all" {
235; CHECK-LABEL: all_of_the_above:
236; CHECK:       @ %bb.0: @ %entry
237; CHECK-NEXT:    .save {r4, r5, r6, r7, lr}
238; CHECK-NEXT:    push {r4, r5, r6, r7, lr}
239; CHECK-NEXT:    .setfp r7, sp, #12
240; CHECK-NEXT:    add r7, sp, #12
241; CHECK-NEXT:    mov r6, r11
242; CHECK-NEXT:    mov r5, r10
243; CHECK-NEXT:    mov r4, r9
244; CHECK-NEXT:    .save {r9, r10, r11}
245; CHECK-NEXT:    push {r4, r5, r6}
246; CHECK-NEXT:    mov r6, r8
247; CHECK-NEXT:    .save {r8}
248; CHECK-NEXT:    push {r6}
249; CHECK-NEXT:    .pad #4
250; CHECK-NEXT:    sub sp, #4
251; CHECK-NEXT:    mov r4, lr
252; CHECK-NEXT:    str r4, [sp] @ 4-byte Spill
253; CHECK-NEXT:    @APP
254; CHECK-NEXT:    @NO_APP
255; CHECK-NEXT:    movs r1, #2
256; CHECK-NEXT:    movs r2, #3
257; CHECK-NEXT:    movs r3, #4
258; CHECK-NEXT:    ldr r0, [sp] @ 4-byte Reload
259; CHECK-NEXT:    add sp, #4
260; CHECK-NEXT:    pop {r4, r5, r6}
261; CHECK-NEXT:    mov r8, r4
262; CHECK-NEXT:    mov r9, r5
263; CHECK-NEXT:    mov r10, r6
264; CHECK-NEXT:    pop {r4}
265; CHECK-NEXT:    mov r11, r4
266; CHECK-NEXT:    pop {r4, r5, r6, r7, pc}
267entry:
268  tail call void asm sideeffect "", "r,r,r,r,~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d)
269  %e = tail call ptr @llvm.returnaddress(i32 0)
270  %f = ptrtoint ptr %e to i32
271  %vecinit = insertelement <4 x i32> undef, i32 %f, i32 0
272  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
273  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
274  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
275  ret <4 x i32> %vecinit13
276}
277
278; When a base pointer is being used, we can safely use it for saving/restoring
279; the high regs because it is set after the last push, and not used at all in the
280; epliogue. We can also use r4 for restoring the registers despite it also being
281; used when restoring sp from fp, as that happens before the first pop.
282define <4 x i32> @base_pointer(i32 %a) {
283; CHECK-LABEL: base_pointer:
284; CHECK:       @ %bb.0: @ %entry
285; CHECK-NEXT:    .save {r4, r6, r7, lr}
286; CHECK-NEXT:    push {r4, r6, r7, lr}
287; CHECK-NEXT:    .setfp r7, sp, #8
288; CHECK-NEXT:    add r7, sp, #8
289; CHECK-NEXT:    mov lr, r9
290; CHECK-NEXT:    mov r6, r8
291; CHECK-NEXT:    .save {r8, r9}
292; CHECK-NEXT:    push {r6, lr}
293; CHECK-NEXT:    mov r6, sp
294; CHECK-NEXT:    lsls r0, r0, #2
295; CHECK-NEXT:    adds r0, r0, #7
296; CHECK-NEXT:    movs r1, #7
297; CHECK-NEXT:    bics r0, r1
298; CHECK-NEXT:    mov r1, sp
299; CHECK-NEXT:    subs r0, r1, r0
300; CHECK-NEXT:    mov sp, r0
301; CHECK-NEXT:    @APP
302; CHECK-NEXT:    @NO_APP
303; CHECK-NEXT:    movs r0, #1
304; CHECK-NEXT:    movs r1, #2
305; CHECK-NEXT:    movs r2, #3
306; CHECK-NEXT:    movs r3, #4
307; CHECK-NEXT:    subs r6, r7, #7
308; CHECK-NEXT:    subs r6, #9
309; CHECK-NEXT:    mov sp, r6
310; CHECK-NEXT:    pop {r4, r6}
311; CHECK-NEXT:    mov r8, r4
312; CHECK-NEXT:    mov r9, r6
313; CHECK-NEXT:    pop {r4, r6, r7, pc}
314entry:
315  %b = alloca i32, i32 %a
316  call void asm sideeffect "", "r,~{r8},~{r9}"(ptr %b)
317  %vecinit = insertelement <4 x i32> undef, i32 1, i32 0
318  %vecinit11 = insertelement <4 x i32> %vecinit, i32 2, i32 1
319  %vecinit12 = insertelement <4 x i32> %vecinit11, i32 3, i32 2
320  %vecinit13 = insertelement <4 x i32> %vecinit12, i32 4, i32 3
321  ret <4 x i32> %vecinit13
322}
323