1 ; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none %s -o - | FileCheck %s
3 ; struct S { int x[128]; } s;
4 ; int f(int *, int, int, int, struct S);
5 ; int g(int *, int, int, int, int, int);
6 ; int h(int *, int *, int *);
7 ; int u(int *, int *, int *, struct S, struct S);
9 %struct.S = type { [128 x i32] }
10 %struct.__va_list = type { i8* }
12 @s = common dso_local global %struct.S zeroinitializer, align 4
14 declare void @llvm.va_start(i8*)
15 declare dso_local i32 @g(i32*, i32, i32, i32, i32, i32) local_unnamed_addr
16 declare dso_local i32 @f(i32*, i32, i32, i32, %struct.S* byval align 4) local_unnamed_addr
17 declare dso_local i32 @h(i32*, i32*, i32*) local_unnamed_addr
18 declare dso_local i32 @u(i32*, i32*, i32*, %struct.S* byval align 4, %struct.S* byval align 4) local_unnamed_addr
21 ; Test access to arguments, passed on stack (including varargs)
24 ; Usual case, access via SP
25 ; int test_args_sp(int a, int b, int c, int d, int e) {
27 ; return g(v, a, b, c, d, e);
29 define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
31 %v = alloca [4 x i32], align 4
32 %0 = bitcast [4 x i32]* %v to i8*
33 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
34 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
37 ; CHECK-LABEL: test_args_sp
39 ; CHECK: ldr r0, [sp, #40]
40 ; CHECK-NEXT: mov r5, sp
41 ; CHECK-NEXT: str r3, [r5]
43 ; CHECK-NEXT: str r0, [r5, #4]
46 ; int test_varargs_sp(int a, ...) {
48 ; __builtin_va_list ap;
49 ; __builtin_va_start(ap, a);
50 ; return g(v, a, 0, 0, 0, 0);
52 define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr {
54 %v = alloca [4 x i32], align 4
55 %ap = alloca %struct.__va_list, align 4
56 %0 = bitcast [4 x i32]* %v to i8*
57 %1 = bitcast %struct.__va_list* %ap to i8*
58 call void @llvm.va_start(i8* nonnull %1)
59 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
60 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0)
63 ; CHECK-LABEL: test_varargs_sp
64 ; Three incoming varargs in registers
67 ; Incoming arguments area is accessed via SP
68 ; CHECK: add r0, sp, #36
69 ; CHECK: stm r0!, {r1, r2, r3}
71 ; Re-aligned stack, access via FP
72 ; int test_args_realign(int a, int b, int c, int d, int e) {
73 ; __attribute__((aligned(16))) int v[4];
74 ; return g(v, a, b, c, d, e);
76 ; Function Attrs: nounwind
77 define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
79 %v = alloca [4 x i32], align 16
80 %0 = bitcast [4 x i32]* %v to i8*
81 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
82 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
85 ; CHECK-LABEL: test_args_realign
87 ; CHECK: add r7, sp, #8
90 ; CHECK-NEXT: lsrs r4, r4, #4
91 ; CHECK-NEXT: lsls r4, r4, #4
92 ; CHECK-NEXT: mov sp, r4
94 ; CHECK: ldr r0, [r7, #8]
95 ; CHECK-NEXT: mov r5, sp
96 ; CHECK-NEXT: str r3, [r5]
97 ; Pass `e` as argument
98 ; CHECK-NEXT: str r0, [r5, #4]
101 ; int test_varargs_realign(int a, ...) {
102 ; __attribute__((aligned(16))) int v[4];
103 ; __builtin_va_list ap;
104 ; __builtin_va_start(ap, a);
105 ; return g(v, a, 0, 0, 0, 0);
107 define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr {
109 %v = alloca [4 x i32], align 16
110 %ap = alloca %struct.__va_list, align 4
111 %0 = bitcast [4 x i32]* %v to i8*
112 %1 = bitcast %struct.__va_list* %ap to i8*
113 call void @llvm.va_start(i8* nonnull %1)
114 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
115 %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0)
118 ; CHECK-LABEL: test_varargs_realign
119 ; Three incoming register varargs
121 ; Setup frame pointer
122 ; CHECK: add r7, sp, #8
125 ; CHECK-NEXT: lsrs r4, r4, #4
126 ; CHECK-NEXT: lsls r4, r4, #4
127 ; CHECK-NEXT: mov sp, r4
128 ; Incoming register varargs stored via FP
130 ; CHECK-NEXT: adds r0, #8
131 ; CHECK-NEXT: stm r0!, {r1, r2, r3}
132 ; VLAs present, access via FP
133 ; int test_args_vla(int a, int b, int c, int d, int e) {
135 ; return g(v, a, b, c, d, e);
137 define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
139 %vla = alloca i32, i32 %a, align 4
140 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
143 ; CHECK-LABEL: test_args_vla
144 ; Setup frame pointer
145 ; CHECK: add r7, sp, #12
146 ; Allocate outgoing stack arguments space
149 ; CHECK: ldr r5, [r7, #8]
150 ; CHECK-NEXT: mov r0, sp
151 ; Pass `d` and `e` as arguments
152 ; CHECK-NEXT: stm r0!, {r3, r5}
155 ; int test_varargs_vla(int a, ...) {
157 ; __builtin_va_list ap;
158 ; __builtin_va_start(ap, a);
159 ; return g(v, a, 0, 0, 0, 0);
161 define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr {
163 %ap = alloca %struct.__va_list, align 4
164 %vla = alloca i32, i32 %a, align 4
165 %0 = bitcast %struct.__va_list* %ap to i8*
166 call void @llvm.va_start(i8* nonnull %0)
167 %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0)
170 ; CHECK-LABEL: test_varargs_vla
171 ; Three incoming register varargs
173 ; Setup frame pointer
174 ; CHECK: add r7, sp, #8
175 ; Register varargs stored via FP
176 ; CHECK: str r3, [r7, #16]
177 ; CHECK-NEXT: str r2, [r7, #12]
178 ; CHECK-NEXT: str r1, [r7, #8]
180 ; Moving SP, access via SP
181 ; int test_args_moving_sp(int a, int b, int c, int d, int e) {
183 ; return f(v, a, b + c + d, e, s) + h(v, v+1, v+2);
185 define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
187 %v = alloca [4 x i32], align 4
188 %0 = bitcast [4 x i32]* %v to i8*
189 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
190 %add = add nsw i32 %c, %b
191 %add1 = add nsw i32 %add, %d
192 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 %add1, i32 %e, %struct.S* byval nonnull align 4 @s)
193 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1
194 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2
195 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5)
196 %add7 = add nsw i32 %call6, %call
199 ; CHECK-LABEL: test_args_moving_sp
200 ; 20 bytes callee-saved area
201 ; CHECK: push {r4, r5, r6, r7, lr}
204 ; Allocate outgoing arguments space
205 ; CHECK: sub sp, #508
207 ; Load `e` via SP, 552 = 512 + 20 + 20
208 ; CHECK: ldr r3, [sp, #552]
210 ; Stack restored before next call
211 ; CHECK-NEXT: add sp, #508
212 ; CHECK-NEXT: add sp, #4
215 ; int test_varargs_moving_sp(int a, ...) {
217 ; __builtin_va_list ap;
218 ; __builtin_va_start(ap, a);
219 ; return f(v, a, 0, 0, s) + h(v, v+1, v+2);
221 define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr {
223 %v = alloca [4 x i32], align 4
224 %ap = alloca %struct.__va_list, align 4
225 %0 = bitcast [4 x i32]* %v to i8*
226 %1 = bitcast %struct.__va_list* %ap to i8*
227 call void @llvm.va_start(i8* nonnull %1)
228 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
229 %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, %struct.S* byval nonnull align 4 @s)
230 %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1
231 %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2
232 %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5)
233 %add = add nsw i32 %call6, %call
236 ; CHECK-LABEL: test_varargs_moving_sp
237 ; Three incoming register varargs
239 ; 16 bytes callee-saves
240 ; CHECK: push {r4, r5, r7, lr}
243 ; Incoming varargs stored via SP, 36 = 20 + 16
244 ; CHECK: add r0, sp, #36
245 ; CHECK-NEXT: stm r0!, {r1, r2, r3}
251 ; Usual case, access via SP.
252 ; int test_local(int n) {
256 ; return g(v, x, y, z, 0, 0);
258 define dso_local i32 @test_local(i32 %n) local_unnamed_addr {
260 %v = alloca [4 x i32], align 4
261 %x = alloca i32, align 4
262 %y = alloca i32, align 4
263 %z = alloca i32, align 4
264 %0 = bitcast [4 x i32]* %v to i8*
265 %1 = bitcast i32* %x to i8*
266 %2 = bitcast i32* %y to i8*
267 %3 = bitcast i32* %z to i8*
268 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
269 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
270 %4 = load i32, i32* %x, align 4
271 %5 = load i32, i32* %y, align 4
272 %6 = load i32, i32* %z, align 4
273 %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0)
276 ; CHECK-LABEL: test_local
277 ; Arguments to `h` relative to SP
278 ; CHECK: add r0, sp, #20
279 ; CHECK-NEXT: add r1, sp, #16
280 ; CHECK-NEXT: add r2, sp, #12
282 ; Load `x`, `y`, and `z` via SP
283 ; CHECK: ldr r1, [sp, #20]
284 ; CHECK-NEXT: ldr r2, [sp, #16]
285 ; CHECK-NEXT: ldr r3, [sp, #12]
288 ; Re-aligned stack, access via SP.
289 ; int test_local_realign(int n) {
290 ; __attribute__((aligned(16))) int v[4];
293 ; return g(v, x, y, z, 0, 0);
295 define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr {
297 %v = alloca [4 x i32], align 16
298 %x = alloca i32, align 4
299 %y = alloca i32, align 4
300 %z = alloca i32, align 4
301 %0 = bitcast [4 x i32]* %v to i8*
302 %1 = bitcast i32* %x to i8*
303 %2 = bitcast i32* %y to i8*
304 %3 = bitcast i32* %z to i8*
305 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
306 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
307 %4 = load i32, i32* %x, align 4
308 %5 = load i32, i32* %y, align 4
309 %6 = load i32, i32* %z, align 4
310 %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0)
313 ; CHECK-LABEL: test_local_realign
314 ; Setup frame pointer
315 ; CHECK: add r7, sp, #8
318 ; CHECK-NEXT: lsrs r4, r4, #4
319 ; CHECK-NEXT: lsls r4, r4, #4
320 ; CHECK-NEXT: mov sp, r4
321 ; Arguments to `h` computed relative to SP
322 ; CHECK: add r0, sp, #28
323 ; CHECK-NEXT: add r1, sp, #24
324 ; CHECK-NEXT: add r2, sp, #20
326 ; Load `x`, `y`, and `z` via SP for passing to `g`
327 ; CHECK: ldr r1, [sp, #28]
328 ; CHECK-NEXT: ldr r2, [sp, #24]
329 ; CHECK-NEXT: ldr r3, [sp, #20]
332 ; VLAs, access via BP.
333 ; int test_local_vla(int n) {
337 ; return g(v, x, y, z, 0, 0);
339 define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr {
341 %x = alloca i32, align 4
342 %y = alloca i32, align 4
343 %z = alloca i32, align 4
344 %vla = alloca i32, i32 %n, align 4
345 %0 = bitcast i32* %x to i8*
346 %1 = bitcast i32* %y to i8*
347 %2 = bitcast i32* %z to i8*
348 %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
349 %3 = load i32, i32* %x, align 4
350 %4 = load i32, i32* %y, align 4
351 %5 = load i32, i32* %z, align 4
352 %call1 = call i32 @g(i32* nonnull %vla, i32 %3, i32 %4, i32 %5, i32 0, i32 0)
355 ; CHECK-LABEL: test_local_vla
356 ; Setup frame pointer
357 ; CHECK: add r7, sp, #12
361 ; Arguments to `h` compute relative to BP
362 ; CHECK: adds r0, r6, #7
363 ; CHECK-NEXT: adds r0, #1
364 ; CHECK-NEXT: adds r1, r6, #4
365 ; CHECK-NEXT: mov r2, r6
367 ; Load `x`, `y`, `z` via BP (r5 should still have the value of r6 from the move
369 ; CHECK: ldr r3, [r5]
370 ; CHECK-NEXT: ldr r2, [r5, #4]
371 ; CHECK-NEXT: ldr r1, [r5, #8]
374 ; Moving SP, access via SP.
375 ; int test_local_moving_sp(int n) {
378 ; return u(v, &x, &y, s, s) + u(v, &y, &z, s, s);
380 define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr {
382 %v = alloca [4 x i32], align 4
383 %x = alloca i32, align 4
384 %y = alloca i32, align 4
385 %z = alloca i32, align 4
386 %0 = bitcast [4 x i32]* %v to i8*
387 %1 = bitcast i32* %x to i8*
388 %2 = bitcast i32* %y to i8*
389 %3 = bitcast i32* %z to i8*
390 %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
391 %call = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %x, i32* nonnull %y, %struct.S* byval nonnull align 4 @s, %struct.S* byval nonnull align 4 @s)
392 %call2 = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %y, i32* nonnull %z, %struct.S* byval nonnull align 4 @s, %struct.S* byval nonnull align 4 @s)
393 %add = add nsw i32 %call2, %call
396 ; CHECK-LABEL: test_local_moving_sp
400 ; CHECK: sub sp, #508
401 ; CHECK-NEXT: sub sp, #508
402 ; CHECK-NEXT: sub sp, #8
403 ; Argument addresses computed relative to SP
404 ; CHECK: add r4, sp, #1020
405 ; CHECK-NEXT: adds r4, #24
406 ; CHECK: add r1, sp, #1020
407 ; CHECK-NEXT: adds r1, #20
408 ; CHECK: add r5, sp, #1020
409 ; CHECK-NEXT: adds r5, #16
411 ; Stack restored before next call
412 ; CHECK: add sp, #508
413 ; CHECK-NEXT: add sp, #508
414 ; CHECK-NEXT: add sp, #8