1 ; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-NOFP,CHECK-ATPCS
2 ; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=all %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-FP-ATPCS,CHECK-ATPCS
3 ; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none -mattr=+aapcs-frame-chain %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-NOFP,CHECK-AAPCS
4 ; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=all -mattr=+aapcs-frame-chain %s -o - --verify-machineinstrs | FileCheck %s --check-prefixes=CHECK,CHECK-FP-AAPCS,CHECK-AAPCS
6 ; struct S { int x[128]; } s;
7 ; int f(int *, int, int, int, struct S);
8 ; int g(int *, int, int, int, int, int);
9 ; int h(int *, int *, int *);
10 ; int u(int *, int *, int *, struct S, struct S);
12 %struct.S = type { [128 x i32] }
13 %struct.__va_list = type { ptr }
15 @s = common dso_local global %struct.S zeroinitializer, align 4
17 declare void @llvm.va_start(ptr)
18 declare dso_local i32 @i(i32) local_unnamed_addr
19 declare dso_local i32 @g(ptr, i32, i32, i32, i32, i32) local_unnamed_addr
20 declare dso_local i32 @f(ptr, i32, i32, i32, ptr byval(%struct.S) align 4) local_unnamed_addr
21 declare dso_local i32 @h(ptr, ptr, ptr) local_unnamed_addr
22 declare dso_local i32 @u(ptr, ptr, ptr, ptr byval(%struct.S) align 4, ptr byval(%struct.S) align 4) local_unnamed_addr
25 ; Test access to arguments, passed on stack (including varargs)
28 ; Usual case, access via SP if FP is not available
29 ; int test_args_sp(int a, int b, int c, int d, int e) {
31 ; return g(v, a, b, c, d, e);
33 define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
35 %v = alloca [4 x i32], align 4
36 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
39 ; CHECK-LABEL: test_args_sp
41 ; CHECK-NOFP: ldr r0, [sp, #32]
42 ; CHECK-FP-ATPCS: ldr r0, [r7, #8]
43 ; CHECK-FP-AAPCS: mov r0, r11
44 ; CHECK-FP-AAPCS: ldr r0, [r0, #8]
45 ; CHECK-NEXT: str r3, [sp]
47 ; CHECK-NEXT: str r0, [sp, #4]
50 ; int test_varargs_sp(int a, ...) {
52 ; __builtin_va_list ap;
53 ; __builtin_va_start(ap, a);
54 ; return g(v, a, 0, 0, 0, 0);
56 define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr {
58 %v = alloca [4 x i32], align 4
59 %ap = alloca %struct.__va_list, align 4
60 call void @llvm.va_start(ptr nonnull %ap)
61 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 0, i32 0, i32 0, i32 0)
64 ; CHECK-LABEL: test_varargs_sp
65 ; Three incoming varargs in registers
68 ; Incoming arguments area is accessed via SP if FP is not available
69 ; CHECK-NOFP: add r0, sp, #36
70 ; CHECK-NOFP: stm r0!, {r1, r2, r3}
71 ; CHECK-FP-ATPCS: mov r0, r7
72 ; CHECK-FP-ATPCS: adds r0, #8
73 ; CHECK-FP-ATPCS: stm r0!, {r1, r2, r3}
74 ; CHECK-FP-AAPCS: mov r0, r11
75 ; CHECK-FP-AAPCS: mov r7, r0
76 ; CHECK-FP-AAPCS: adds r7, #8
77 ; CHECK-FP-AAPCS: stm r7!, {r1, r2, r3}
78 ; Re-aligned stack, access via FP
79 ; int test_args_realign(int a, int b, int c, int d, int e) {
80 ; __attribute__((aligned(16))) int v[4];
81 ; return g(v, a, b, c, d, e);
83 ; Function Attrs: nounwind
84 define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
86 %v = alloca [4 x i32], align 16
87 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
90 ; CHECK-LABEL: test_args_realign
92 ; CHECK-ATPCS: add r7, sp, #8
93 ; CHECK-AAPCS: mov r11, sp
96 ; CHECK-NEXT: lsrs r4, r4, #4
97 ; CHECK-NEXT: lsls r4, r4, #4
98 ; CHECK-NEXT: mov sp, r4
100 ; CHECK-ATPCS: ldr r0, [r7, #8]
101 ; CHECK-AAPCS: mov r0, r11
102 ; CHECK-AAPCS: ldr r0, [r0, #8]
103 ; CHECK-NEXT: str r3, [sp]
104 ; Pass `e` as argument
105 ; CHECK-NEXT: str r0, [sp, #4]
108 ; int test_varargs_realign(int a, ...) {
109 ; __attribute__((aligned(16))) int v[4];
110 ; __builtin_va_list ap;
111 ; __builtin_va_start(ap, a);
112 ; return g(v, a, 0, 0, 0, 0);
114 define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr {
116 %v = alloca [4 x i32], align 16
117 %ap = alloca %struct.__va_list, align 4
118 call void @llvm.va_start(ptr nonnull %ap)
119 %call = call i32 @g(ptr nonnull %v, i32 %a, i32 0, i32 0, i32 0, i32 0)
122 ; CHECK-LABEL: test_varargs_realign
123 ; Three incoming register varargs
125 ; Setup frame pointer
126 ; CHECK-ATPCS: add r7, sp, #8
127 ; CHECK-AAPCS: mov r11, sp
130 ; CHECK-NEXT: lsrs r4, r4, #4
131 ; CHECK-NEXT: lsls r4, r4, #4
132 ; CHECK-NEXT: mov sp, r4
133 ; Incoming register varargs stored via FP
134 ; CHECK-ATPCS: mov r0, r7
135 ; CHECK-ATPCS-NEXT: adds r0, #8
136 ; CHECK-ATPCS-NEXT: stm r0!, {r1, r2, r3}
137 ; CHECK-AAPCS: mov r0, r11
138 ; CHECK-AAPCS: mov r7, r0
139 ; CHECK-AAPCS: adds r7, #8
140 ; CHECK-AAPCS: stm r7!, {r1, r2, r3}
141 ; VLAs present, access via FP
142 ; int test_args_vla(int a, int b, int c, int d, int e) {
144 ; return g(v, a, b, c, d, e);
146 define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
148 %vla = alloca i32, i32 %a, align 4
149 %call = call i32 @g(ptr nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
152 ; CHECK-LABEL: test_args_vla
153 ; Setup frame pointer
154 ; CHECK-ATPCS: add r7, sp, #12
155 ; CHECK-AAPCS: mov r11, sp
156 ; Allocate outgoing stack arguments space
159 ; CHECK-ATPCS: ldr r5, [r7, #8]
160 ; CHECK-AAPCS: mov r5, r11
161 ; CHECK-AAPCS: ldr r5, [r5, #8]
162 ; Pass `d` and `e` as arguments
163 ; CHECK-NEXT: str r3, [sp]
164 ; CHECK-NEXT: str r5, [sp, #4]
167 ; int test_varargs_vla(int a, ...) {
169 ; __builtin_va_list ap;
170 ; __builtin_va_start(ap, a);
171 ; return g(v, a, 0, 0, 0, 0);
173 define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr {
175 %ap = alloca %struct.__va_list, align 4
176 %vla = alloca i32, i32 %a, align 4
177 call void @llvm.va_start(ptr nonnull %ap)
178 %call = call i32 @g(ptr nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0)
181 ; CHECK-LABEL: test_varargs_vla
182 ; Three incoming register varargs
184 ; Setup frame pointer
185 ; CHECK-ATPCS: add r7, sp, #8
186 ; CHECK-AAPCS: mov r11, sp
187 ; Register varargs stored via FP
188 ; CHECK-ATPCS-DAG: str r3, [r7, #16]
189 ; CHECK-ATPCS-DAG: str r2, [r7, #12]
190 ; CHECK-ATPCS-DAG: str r1, [r7, #8]
191 ; CHECK-AAPCS-DAG: mov r5, r11
192 ; CHECK-AAPCS-DAG: str r1, [r5, #8]
193 ; CHECK-AAPCS-DAG: mov r1, r11
194 ; CHECK-AAPCS-DAG: str r3, [r1, #16]
195 ; CHECK-AAPCS-DAG: mov r1, r11
196 ; CHECK-AAPCS-DAG: str r2, [r1, #12]
198 ; Moving SP, access via SP
199 ; int test_args_moving_sp(int a, int b, int c, int d, int e) {
201 ; return f(v, a, b + c + d, e, s) + h(v, v+1, v+2);
203 define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
205 %v = alloca [4 x i32], align 4
206 %add = add nsw i32 %c, %b
207 %add1 = add nsw i32 %add, %d
208 %call = call i32 @f(ptr nonnull %v, i32 %a, i32 %add1, i32 %e, ptr byval(%struct.S) nonnull align 4 @s)
209 %add.ptr = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 1
210 %add.ptr5 = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 2
211 %call6 = call i32 @h(ptr nonnull %v, ptr nonnull %add.ptr, ptr nonnull %add.ptr5)
212 %add7 = add nsw i32 %call6, %call
215 ; CHECK-LABEL: test_args_moving_sp
216 ; 20 bytes callee-saved area without FP
217 ; CHECK-NOFP: push {r4, r5, r6, r7, lr}
218 ; 20 bytes callee-saved area for ATPCS
219 ; CHECK-FP-ATPCS: push {r4, r5, r6, r7, lr}
220 ; 24 bytes callee-saved area for AAPCS as codegen prefers an even number of GPRs spilled
221 ; CHECK-FP-AAPCS: push {lr}
222 ; CHECK-FP-AAPCS: mov lr, r11
223 ; CHECK-FP-AAPCS: push {lr}
224 ; CHECK-FP-AAPCS: push {r4, r5, r6, r7}
225 ; 20 bytes locals without FP
226 ; CHECK-NOFP: sub sp, #20
227 ; 28 bytes locals with FP for ATPCS
228 ; CHECK-FP-ATPCS: sub sp, #28
229 ; 24 bytes locals with FP for AAPCS
230 ; CHECK-FP-AAPCS: sub sp, #24
233 ; Allocate outgoing arguments space
234 ; CHECK: sub sp, #508
236 ; Load `e` via BP if FP is not present (40 = 20 + 20)
237 ; CHECK-NOFP: ldr r3, [r6, #40]
238 ; Load `e` via FP otherwise
239 ; CHECK-FP-ATPCS: ldr r3, [r7, #8]
240 ; CHECK-FP-AAPCS: mov r0, r11
241 ; CHECK-FP-AAPCS: ldr r3, [r0, #8]
243 ; Stack restored before next call
244 ; CHECK-NEXT: add sp, #508
245 ; CHECK-NEXT: add sp, #4
248 ; int test_varargs_moving_sp(int a, ...) {
250 ; __builtin_va_list ap;
251 ; __builtin_va_start(ap, a);
252 ; return f(v, a, 0, 0, s) + h(v, v+1, v+2);
254 define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr {
256 %v = alloca [4 x i32], align 4
257 %ap = alloca %struct.__va_list, align 4
258 call void @llvm.va_start(ptr nonnull %ap)
259 %call = call i32 @f(ptr nonnull %v, i32 %a, i32 0, i32 0, ptr byval(%struct.S) nonnull align 4 @s)
260 %add.ptr = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 1
261 %add.ptr5 = getelementptr inbounds [4 x i32], ptr %v, i32 0, i32 2
262 %call6 = call i32 @h(ptr nonnull %v, ptr nonnull %add.ptr, ptr nonnull %add.ptr5)
263 %add = add nsw i32 %call6, %call
266 ; CHECK-LABEL: test_varargs_moving_sp
267 ; Three incoming register varargs
269 ; 16 bytes callee-saves without FP
270 ; CHECK-NOFP: push {r4, r5, r6, lr}
271 ; 24 bytes callee-saves with FP
272 ; CHECK-FP-ATPCS: push {r4, r5, r6, r7, lr}
273 ; CHECK-FP-AAPCS: push {lr}
274 ; CHECK-FP-AAPCS: mov lr, r11
275 ; CHECK-FP-AAPCS: push {lr}
276 ; CHECK-FP-AAPCS: push {r4, r5, r6, r7}
278 ; CHECK-NOFP: sub sp, #20
279 ; CHECK-FP-ATPCS: sub sp, #24
280 ; CHECK-FP-AAPCS: sub sp, #20
281 ; Incoming varargs stored via BP if FP is not present (36 = 20 + 16)
282 ; CHECK-NOFP: mov r0, r6
283 ; CHECK-NOFP-NEXT: adds r0, #36
284 ; CHECK-NOFP-NEXT: stm r0!, {r1, r2, r3}
285 ; Incoming varargs stored via FP otherwise
286 ; CHECK-FP-ATPCS: mov r0, r7
287 ; CHECK-FP-ATPCS-NEXT: adds r0, #8
288 ; CHECK-FP-ATPCS-NEXT: stm r0!, {r1, r2, r3}
289 ; CHECK-FP-AAPCS: mov r0, r11
290 ; CHECK-FP-AAPCS-NEXT: mov r5, r0
291 ; CHECK-FP-AAPCS-NEXT: adds r5, #8
292 ; CHECK-FP-AAPCS-NEXT: stm r5!, {r1, r2, r3}
294 ; struct S { int x[128]; } s;
295 ; int test(S a, int b) {
298 define dso_local i32 @test_args_large_offset(ptr byval(%struct.S) align 4 %0, i32 %1) local_unnamed_addr {
299 %3 = alloca i32, align 4
300 store i32 %1, ptr %3, align 4
301 %4 = load i32, ptr %3, align 4
302 %5 = call i32 @i(i32 %4)
305 ; CHECK-LABEL: test_args_large_offset
306 ; Without FP: Access to large offset is made using SP
307 ; CHECK-NOFP: ldr r0, [sp, #520]
308 ; With FP: Access to large offset is made through a const pool using FP
309 ; CHECK-FP: ldr r0, .LCPI0_0
310 ; CHECK-FP-ATPCS: ldr r0, [r0, r7]
311 ; CHECK-FP-AAPCS: add r0, r11
312 ; CHECK-FP-AAPCS: ldr r0, [r0]
319 ; Usual case, access via SP.
320 ; int test_local(int n) {
324 ; return g(v, x, y, z, 0, 0);
326 define dso_local i32 @test_local(i32 %n) local_unnamed_addr {
328 %v = alloca [4 x i32], align 4
329 %x = alloca i32, align 4
330 %y = alloca i32, align 4
331 %z = alloca i32, align 4
332 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z)
333 %0 = load i32, ptr %x, align 4
334 %1 = load i32, ptr %y, align 4
335 %2 = load i32, ptr %z, align 4
336 %call1 = call i32 @g(ptr nonnull %v, i32 %0, i32 %1, i32 %2, i32 0, i32 0)
339 ; CHECK-LABEL: test_local
340 ; Arguments to `h` relative to SP
341 ; CHECK: add r0, sp, #20
342 ; CHECK-NEXT: add r1, sp, #16
343 ; CHECK-NEXT: add r2, sp, #12
345 ; Load `x`, `y`, and `z` via SP
346 ; CHECK: ldr r1, [sp, #20]
347 ; CHECK-NEXT: ldr r2, [sp, #16]
348 ; CHECK-NEXT: ldr r3, [sp, #12]
351 ; Re-aligned stack, access via SP.
352 ; int test_local_realign(int n) {
353 ; __attribute__((aligned(16))) int v[4];
356 ; return g(v, x, y, z, 0, 0);
358 define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr {
360 %v = alloca [4 x i32], align 16
361 %x = alloca i32, align 4
362 %y = alloca i32, align 4
363 %z = alloca i32, align 4
364 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z)
365 %0 = load i32, ptr %x, align 4
366 %1 = load i32, ptr %y, align 4
367 %2 = load i32, ptr %z, align 4
368 %call1 = call i32 @g(ptr nonnull %v, i32 %0, i32 %1, i32 %2, i32 0, i32 0)
371 ; CHECK-LABEL: test_local_realign
372 ; Setup frame pointer
373 ; CHECK-ATPCS: add r7, sp, #8
374 ; CHECK-AAPCS: mov r11, sp
377 ; CHECK-NEXT: lsrs r4, r4, #4
378 ; CHECK-NEXT: lsls r4, r4, #4
379 ; CHECK-NEXT: mov sp, r4
380 ; Arguments to `h` computed relative to SP
381 ; CHECK: add r0, sp, #28
382 ; CHECK-NEXT: add r1, sp, #24
383 ; CHECK-NEXT: add r2, sp, #20
385 ; Load `x`, `y`, and `z` via SP for passing to `g`
386 ; CHECK: ldr r1, [sp, #28]
387 ; CHECK-NEXT: ldr r2, [sp, #24]
388 ; CHECK-NEXT: ldr r3, [sp, #20]
391 ; VLAs, access via BP.
392 ; int test_local_vla(int n) {
396 ; return g(v, x, y, z, 0, 0);
398 define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr {
400 %x = alloca i32, align 4
401 %y = alloca i32, align 4
402 %z = alloca i32, align 4
403 %vla = alloca i32, i32 %n, align 4
404 %call = call i32 @h(ptr nonnull %x, ptr nonnull %y, ptr nonnull %z)
405 %0 = load i32, ptr %x, align 4
406 %1 = load i32, ptr %y, align 4
407 %2 = load i32, ptr %z, align 4
408 %call1 = call i32 @g(ptr nonnull %vla, i32 %0, i32 %1, i32 %2, i32 0, i32 0)
411 ; CHECK-LABEL: test_local_vla
412 ; Setup frame pointer
413 ; CHECK-ATPCS: add r7, sp, #12
414 ; CHECK-AAPCS: mov r11, sp
416 ; CHECK-ATPCS: sub sp, #12
417 ; CHECK-AAPCS: sub sp, #16
420 ; CHECK-ATPCS: mov r5, r6
421 ; CHECK-AAPCS: adds r5, r6, #4
422 ; Arguments to `h` compute relative to BP
423 ; CHECK: adds r0, r6, #7
424 ; CHECK-ATPCS-NEXT: adds r0, #1
425 ; CHECK-ATPCS-NEXT: adds r1, r6, #4
426 ; CHECK-ATPCS-NEXT: mov r2, r6
427 ; CHECK-AAPCS-NEXT: adds r0, #5
428 ; CHECK-AAPCS-NEXT: adds r1, r6, #7
429 ; CHECK-AAPCS-NEXT: adds r1, #1
430 ; CHECK-AAPCS-NEXT: adds r2, r6, #4
432 ; Load `x`, `y`, `z` via BP (r5 should still have the value of r6 from the move
434 ; CHECK: ldr r3, [r5]
435 ; CHECK-NEXT: ldr r2, [r5, #4]
436 ; CHECK-NEXT: ldr r1, [r5, #8]
439 ; Moving SP, access via SP.
440 ; int test_local_moving_sp(int n) {
443 ; return u(v, &x, &y, s, s) + u(v, &y, &z, s, s);
445 define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr {
447 %v = alloca [4 x i32], align 4
448 %x = alloca i32, align 4
449 %y = alloca i32, align 4
450 %z = alloca i32, align 4
451 %call = call i32 @u(ptr nonnull %v, ptr nonnull %x, ptr nonnull %y, ptr byval(%struct.S) nonnull align 4 @s, ptr byval(%struct.S) nonnull align 4 @s)
452 %call2 = call i32 @u(ptr nonnull %v, ptr nonnull %y, ptr nonnull %z, ptr byval(%struct.S) nonnull align 4 @s, ptr byval(%struct.S) nonnull align 4 @s)
453 %add = add nsw i32 %call2, %call
456 ; CHECK-LABEL: test_local_moving_sp
458 ; CHECK-NOFP: sub sp, #36
459 ; CHECK-FP-ATPCS: sub sp, #44
460 ; CHECK-FP-AAPCS: sub sp, #40
464 ; CHECK: sub sp, #508
465 ; CHECK-NEXT: sub sp, #508
466 ; CHECK-NEXT: sub sp, #8
467 ; Argument addresses computed relative to BP
468 ; CHECK-NOFP: adds r4, r6, #7
469 ; CHECK-NOFP-NEXT: adds r4, #13
470 ; CHECK-NOFP: adds r1, r6, #7
471 ; CHECK-NOFP-NEXT: adds r1, #9
472 ; CHECK-NOFP: adds r5, r6, #7
473 ; CHECK-NOFP-NEXT: adds r5, #5
474 ; CHECK-FP-ATPCS: adds r0, r6, #7
475 ; CHECK-FP-ATPCS-NEXT: adds r0, #21
476 ; CHECK-FP-ATPCS: adds r1, r6, #7
477 ; CHECK-FP-ATPCS-NEXT: adds r1, #17
478 ; CHECK-FP-ATPCS: adds r5, r6, #7
479 ; CHECK-FP-ATPCS-NEXT: adds r5, #13
480 ; CHECK-FP-AAPCS: adds r4, r6, #7
481 ; CHECK-FP-AAPCS-NEXT: adds r4, #17
482 ; CHECK-FP-AAPCS: adds r1, r6, #7
483 ; CHECK-FP-AAPCS-NEXT: adds r1, #13
484 ; CHECK-FP-AAPCS: adds r5, r6, #7
485 ; CHECK-FP-AAPCS-NEXT: adds r5, #9
487 ; Stack restored before next call
488 ; CHECK: add sp, #508
489 ; CHECK-NEXT: add sp, #508
490 ; CHECK-NEXT: add sp, #8