1 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2 ; RUN: llc -mtriple=thumbv6m-none-eabi < %s | FileCheck %s
4 declare ptr @llvm.returnaddress(i32)
6 ; We don't allocate high registers, so any function not using inline asm will
7 ; only need to save the low registers.
8 define 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}
15 ; CHECK-NEXT: pop {r4, r5, r6, r7, pc}
17 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7}"()
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.
23 define 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}
31 ; CHECK-NEXT: pop {r0}
32 ; CHECK-NEXT: mov r8, r0
35 tail call void asm sideeffect "", "~{r8}"()
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.
41 define 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}
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
59 tail call void asm sideeffect "", "~{r8},~{r9},~{r10},~{r11}"()
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.
66 define 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}
76 ; CHECK-NEXT: pop {r0}
77 ; CHECK-NEXT: mov r8, r0
78 ; CHECK-NEXT: pop {r4, r7, pc}
80 tail call void asm sideeffect "", "~{r4},~{r8}"()
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.
86 define 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}
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}
106 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
111 ; All callee-saved registers clobbered, and frame pointer is requested. r7 now
112 ; cannot be used while saving/restoring the high regs.
113 define 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}
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}
135 tail call void asm sideeffect "", "~{r4},~{r5},~{r6},~{r7},~{r8},~{r9},~{r10},~{r11}"()
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
142 define 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
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}
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)
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.
172 define 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}
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}
192 tail call void asm sideeffect "", "r,r,r,r,~{r8},~{r9},~{r10},~{r11}"(i32 %a, i32 %b, i32 %c, i32 %d)
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.
198 define <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}
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}
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
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
234 define <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
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}
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
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.
282 define <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
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}
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