• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1; RUN: llc -mtriple=thumbv6m-eabi -frame-pointer=none %s -o - | FileCheck %s
2
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);
8
9%struct.S = type { [128 x i32] }
10%struct.__va_list = type { i8* }
11
12@s = common dso_local global %struct.S zeroinitializer, align 4
13
14declare void @llvm.va_start(i8*)
15declare dso_local i32 @g(i32*, i32, i32, i32, i32, i32) local_unnamed_addr
16declare dso_local i32 @f(i32*, i32, i32, i32, %struct.S* byval(%struct.S) align 4) local_unnamed_addr
17declare dso_local i32 @h(i32*, i32*, i32*) local_unnamed_addr
18declare dso_local i32 @u(i32*, i32*, i32*, %struct.S* byval(%struct.S) align 4, %struct.S* byval(%struct.S) align 4) local_unnamed_addr
19
20;
21; Test access to arguments, passed on stack (including varargs)
22;
23
24; Usual case, access via SP
25; int test_args_sp(int a, int b, int c, int d, int e) {
26;   int v[4];
27;   return g(v, a, b, c, d, e);
28; }
29define dso_local i32 @test_args_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr {
30entry:
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)
35  ret i32 %call
36}
37; CHECK-LABEL: test_args_sp
38; Load `e`
39; CHECK:       ldr    r0, [sp, #32]
40; CHECK-NEXT:  str    r3, [sp]
41; Pass `e` on stack
42; CHECK-NEXT:  str    r0, [sp, #4]
43; CHECK:       bl    g
44
45; int test_varargs_sp(int a, ...) {
46;   int v[4];
47;   __builtin_va_list ap;
48;   __builtin_va_start(ap, a);
49;   return g(v, a, 0, 0, 0, 0);
50; }
51define dso_local i32 @test_varargs_sp(i32 %a, ...) local_unnamed_addr  {
52entry:
53  %v = alloca [4 x i32], align 4
54  %ap = alloca %struct.__va_list, align 4
55  %0 = bitcast [4 x i32]* %v to i8*
56  %1 = bitcast %struct.__va_list* %ap to i8*
57  call void @llvm.va_start(i8* nonnull %1)
58  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
59  %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0)
60  ret i32 %call
61}
62; CHECK-LABEL: test_varargs_sp
63; Three incoming varargs in registers
64; CHECK:       sub sp, #12
65; CHECK:       sub sp, #28
66; Incoming arguments area is accessed via SP
67; CHECK:       add r0, sp, #36
68; CHECK:       stm r0!, {r1, r2, r3}
69
70; Re-aligned stack, access via FP
71; int test_args_realign(int a, int b, int c, int d, int e) {
72;   __attribute__((aligned(16))) int v[4];
73;   return g(v, a, b, c, d, e);
74; }
75; Function Attrs: nounwind
76define dso_local i32 @test_args_realign(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr  {
77entry:
78  %v = alloca [4 x i32], align 16
79  %0 = bitcast [4 x i32]* %v to i8*
80  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
81  %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
82  ret i32 %call
83}
84; CHECK-LABEL: test_args_realign
85; Setup frame pointer
86; CHECK:       add r7, sp, #8
87; Align stack
88; CHECK:       mov  r4, sp
89; CHECK-NEXT:  lsrs r4, r4, #4
90; CHECK-NEXT:  lsls r4, r4, #4
91; CHECK-NEXT:  mov  sp, r4
92; Load `e` via FP
93; CHECK:       ldr r0, [r7, #8]
94; CHECK-NEXT:  str r3, [sp]
95; Pass `e` as argument
96; CHECK-NEXT:  str r0, [sp, #4]
97; CHECK:       bl    g
98
99; int test_varargs_realign(int a, ...) {
100;   __attribute__((aligned(16))) int v[4];
101;   __builtin_va_list ap;
102;   __builtin_va_start(ap, a);
103;   return g(v, a, 0, 0, 0, 0);
104; }
105define dso_local i32 @test_varargs_realign(i32 %a, ...) local_unnamed_addr  {
106entry:
107  %v = alloca [4 x i32], align 16
108  %ap = alloca %struct.__va_list, align 4
109  %0 = bitcast [4 x i32]* %v to i8*
110  %1 = bitcast %struct.__va_list* %ap to i8*
111  call void @llvm.va_start(i8* nonnull %1)
112  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
113  %call = call i32 @g(i32* nonnull %arraydecay, i32 %a, i32 0, i32 0, i32 0, i32 0)
114  ret i32 %call
115}
116; CHECK-LABEL: test_varargs_realign
117; Three incoming register varargs
118; CHECK:       sub sp, #12
119; Setup frame pointer
120; CHECK:       add r7, sp, #8
121; Align stack
122; CHECK:       mov  r4, sp
123; CHECK-NEXT:  lsrs r4, r4, #4
124; CHECK-NEXT:  lsls r4, r4, #4
125; CHECK-NEXT:  mov  sp, r4
126; Incoming register varargs stored via FP
127; CHECK:      mov r0, r7
128; CHECK-NEXT: adds r0, #8
129; CHECK-NEXT: stm r0!, {r1, r2, r3}
130; VLAs present, access via FP
131; int test_args_vla(int a, int b, int c, int d, int e) {
132;   int v[a];
133;   return g(v, a, b, c, d, e);
134; }
135define dso_local i32 @test_args_vla(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr  {
136entry:
137  %vla = alloca i32, i32 %a, align 4
138  %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 %b, i32 %c, i32 %d, i32 %e)
139  ret i32 %call
140}
141; CHECK-LABEL: test_args_vla
142; Setup frame pointer
143; CHECK:       add r7, sp, #12
144; Allocate outgoing stack arguments space
145; CHECK:       sub sp, #4
146; Load `e` via FP
147; CHECK:       ldr r5, [r7, #8]
148; Pass `d` and `e` as arguments
149; CHECK-NEXT:  str r3, [sp]
150; CHECK-NEXT:  str r5, [sp, #4]
151; CHECK:       bl  g
152
153; int test_varargs_vla(int a, ...) {
154;   int v[a];
155;   __builtin_va_list ap;
156;   __builtin_va_start(ap, a);
157;   return g(v, a, 0, 0, 0, 0);
158; }
159define dso_local i32 @test_varargs_vla(i32 %a, ...) local_unnamed_addr  {
160entry:
161  %ap = alloca %struct.__va_list, align 4
162  %vla = alloca i32, i32 %a, align 4
163  %0 = bitcast %struct.__va_list* %ap to i8*
164  call void @llvm.va_start(i8* nonnull %0)
165  %call = call i32 @g(i32* nonnull %vla, i32 %a, i32 0, i32 0, i32 0, i32 0)
166  ret i32 %call
167}
168; CHECK-LABEL: test_varargs_vla
169; Three incoming register varargs
170; CHECK:       sub sp, #12
171; Setup frame pointer
172; CHECK:       add r7, sp, #8
173; Register varargs stored via FP
174; CHECK-DAG:  str r3, [r7, #16]
175; CHECK-DAG:  str r2, [r7, #12]
176; CHECK-DAG:  str r1, [r7, #8]
177
178; Moving SP, access via SP
179; int test_args_moving_sp(int a, int b, int c, int d, int e) {
180;   int v[4];
181;   return f(v, a, b + c + d, e, s) + h(v, v+1, v+2);
182; }
183define dso_local i32 @test_args_moving_sp(i32 %a, i32 %b, i32 %c, i32 %d, i32 %e) local_unnamed_addr  {
184entry:
185  %v = alloca [4 x i32], align 4
186  %0 = bitcast [4 x i32]* %v to i8*
187  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
188  %add = add nsw i32 %c, %b
189  %add1 = add nsw i32 %add, %d
190  %call = call i32 @f(i32* nonnull %arraydecay, i32 %a, i32 %add1, i32 %e, %struct.S* byval(%struct.S) nonnull align 4 @s)
191  %add.ptr = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 1
192  %add.ptr5 = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 2
193  %call6 = call i32 @h(i32* nonnull %arraydecay, i32* nonnull %add.ptr, i32* nonnull %add.ptr5)
194  %add7 = add nsw i32 %call6, %call
195  ret i32 %add7
196}
197; CHECK-LABEL: test_args_moving_sp
198; 20 bytes callee-saved area
199; CHECK:       push {r4, r5, r6, r7, lr}
200; 20 bytes locals
201; CHECK:       sub sp, #20
202; Setup base pointer
203; CHECK:       mov r6, sp
204; Allocate outgoing arguments space
205; CHECK:       sub sp, #508
206; CHECK:       sub sp, #4
207; Load `e` via BP, 40 = 20 + 20
208; CHECK:       ldr r3, [r6, #40]
209; CHECK:       bl  f
210; Stack restored before next call
211; CHECK-NEXT:  add sp, #508
212; CHECK-NEXT:  add sp, #4
213; CHECK:       bl  h
214
215; int test_varargs_moving_sp(int a, ...) {
216;   int v[4];
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);
220; }
221define dso_local i32 @test_varargs_moving_sp(i32 %a, ...) local_unnamed_addr  {
222entry:
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(%struct.S) 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
234  ret i32 %add
235}
236; CHECK-LABEL: test_varargs_moving_sp
237; Three incoming register varargs
238; CHECK:       sub sp, #12
239; 16 bytes callee-saves
240; CHECK:       push {r4, r5, r6, lr}
241; 20 bytes locals
242; CHECK:       sub sp, #20
243; Incoming varargs stored via BP, 36 = 20 + 16
244; CHECK:       mov r0, r6
245; CHECK-NEXT:  adds r0, #36
246; CHECK-NEXT:  stm r0!, {r1, r2, r3}
247
248;
249; Access to locals
250;
251
252; Usual case, access via SP.
253; int test_local(int n) {
254;   int v[4];
255;   int x, y, z;
256;   h(&x, &y, &z);
257;   return g(v, x, y, z, 0, 0);
258; }
259define dso_local i32 @test_local(i32 %n) local_unnamed_addr  {
260entry:
261  %v = alloca [4 x i32], align 4
262  %x = alloca i32, align 4
263  %y = alloca i32, align 4
264  %z = alloca i32, align 4
265  %0 = bitcast [4 x i32]* %v to i8*
266  %1 = bitcast i32* %x to i8*
267  %2 = bitcast i32* %y to i8*
268  %3 = bitcast i32* %z to i8*
269  %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
270  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
271  %4 = load i32, i32* %x, align 4
272  %5 = load i32, i32* %y, align 4
273  %6 = load i32, i32* %z, align 4
274  %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0)
275  ret i32 %call1
276}
277; CHECK-LABEL: test_local
278; Arguments to `h` relative to SP
279; CHECK:       add r0, sp, #20
280; CHECK-NEXT:  add r1, sp, #16
281; CHECK-NEXT:  add r2, sp, #12
282; CHECK-NEXT:  bl  h
283; Load `x`, `y`, and `z` via SP
284; CHECK:       ldr r1, [sp, #20]
285; CHECK-NEXT:  ldr r2, [sp, #16]
286; CHECK-NEXT:  ldr r3, [sp, #12]
287; CHECK:       bl  g
288
289; Re-aligned stack, access via SP.
290; int test_local_realign(int n) {
291;   __attribute__((aligned(16))) int v[4];
292;   int x, y, z;
293;   h(&x, &y, &z);
294;   return g(v, x, y, z, 0, 0);
295; }
296define dso_local i32 @test_local_realign(i32 %n) local_unnamed_addr  {
297entry:
298  %v = alloca [4 x i32], align 16
299  %x = alloca i32, align 4
300  %y = alloca i32, align 4
301  %z = alloca i32, align 4
302  %0 = bitcast [4 x i32]* %v to i8*
303  %1 = bitcast i32* %x to i8*
304  %2 = bitcast i32* %y to i8*
305  %3 = bitcast i32* %z to i8*
306  %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
307  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
308  %4 = load i32, i32* %x, align 4
309  %5 = load i32, i32* %y, align 4
310  %6 = load i32, i32* %z, align 4
311  %call1 = call i32 @g(i32* nonnull %arraydecay, i32 %4, i32 %5, i32 %6, i32 0, i32 0)
312  ret i32 %call1
313}
314; CHECK-LABEL: test_local_realign
315; Setup frame pointer
316; CHECK:       add r7, sp, #8
317; Re-align stack
318; CHECK:       mov r4, sp
319; CHECK-NEXT:  lsrs r4, r4, #4
320; CHECK-NEXT:  lsls r4, r4, #4
321; CHECK-NEXT:  mov  sp, r4
322; Arguments to `h` computed relative to SP
323; CHECK:       add r0, sp, #28
324; CHECK-NEXT:  add r1, sp, #24
325; CHECK-NEXT:  add r2, sp, #20
326; CHECK-NEXT:  bl  h
327; Load `x`, `y`, and `z` via SP for passing to `g`
328; CHECK:       ldr r1, [sp, #28]
329; CHECK-NEXT:  ldr r2, [sp, #24]
330; CHECK-NEXT:  ldr r3, [sp, #20]
331; CHECK:       bl  g
332
333; VLAs, access via BP.
334; int test_local_vla(int n) {
335;   int v[n];
336;   int x, y, z;
337;   h(&x, &y, &z);
338;   return g(v, x, y, z, 0, 0);
339; }
340define dso_local i32 @test_local_vla(i32 %n) local_unnamed_addr  {
341entry:
342  %x = alloca i32, align 4
343  %y = alloca i32, align 4
344  %z = alloca i32, align 4
345  %vla = alloca i32, i32 %n, align 4
346  %0 = bitcast i32* %x to i8*
347  %1 = bitcast i32* %y to i8*
348  %2 = bitcast i32* %z to i8*
349  %call = call i32 @h(i32* nonnull %x, i32* nonnull %y, i32* nonnull %z)
350  %3 = load i32, i32* %x, align 4
351  %4 = load i32, i32* %y, align 4
352  %5 = load i32, i32* %z, align 4
353  %call1 = call i32 @g(i32* nonnull %vla, i32 %3, i32 %4, i32 %5, i32 0, i32 0)
354  ret i32 %call1
355}
356; CHECK-LABEL: test_local_vla
357; Setup frame pointer
358; CHECK:       add  r7, sp, #12
359; Setup base pointer
360; CHECK:       mov  r6, sp
361; CHECK:       mov  r5, r6
362; Arguments to `h` compute relative to BP
363; CHECK:       adds r0, r6, #7
364; CHECK-NEXT:  adds r0, #1
365; CHECK-NEXT:  adds r1, r6, #4
366; CHECK-NEXT:  mov  r2, r6
367; CHECK-NEXT:  bl   h
368; Load `x`, `y`, `z` via BP (r5 should still have the value of r6 from the move
369; above)
370; CHECK:       ldr r3, [r5]
371; CHECK-NEXT:  ldr r2, [r5, #4]
372; CHECK-NEXT:  ldr r1, [r5, #8]
373; CHECK:       bl  g
374
375;  Moving SP, access via SP.
376; int test_local_moving_sp(int n) {
377;   int v[4];
378;   int x, y, z;
379;   return u(v, &x, &y, s, s) + u(v, &y, &z, s, s);
380; }
381define dso_local i32 @test_local_moving_sp(i32 %n) local_unnamed_addr {
382entry:
383  %v = alloca [4 x i32], align 4
384  %x = alloca i32, align 4
385  %y = alloca i32, align 4
386  %z = alloca i32, align 4
387  %0 = bitcast [4 x i32]* %v to i8*
388  %1 = bitcast i32* %x to i8*
389  %2 = bitcast i32* %y to i8*
390  %3 = bitcast i32* %z to i8*
391  %arraydecay = getelementptr inbounds [4 x i32], [4 x i32]* %v, i32 0, i32 0
392  %call = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %x, i32* nonnull %y, %struct.S* byval(%struct.S) nonnull align 4 @s, %struct.S* byval(%struct.S) nonnull align 4 @s)
393  %call2 = call i32 @u(i32* nonnull %arraydecay, i32* nonnull %y, i32* nonnull %z, %struct.S* byval(%struct.S) nonnull align 4 @s, %struct.S* byval(%struct.S) nonnull align 4 @s)
394  %add = add nsw i32 %call2, %call
395  ret i32 %add
396}
397; CHECK-LABEL: test_local_moving_sp
398; Locals area
399; CHECK:      sub sp, #36
400; Setup BP
401; CHECK:      mov r6, sp
402; Outoging arguments
403; CHECK:      sub sp, #508
404; CHECK-NEXT: sub sp, #508
405; CHECK-NEXT: sub sp, #8
406; Argument addresses computed relative to BP
407; CHECK:      adds r4, r6, #7
408; CHECK-NEXT: adds r4, #13
409; CHECK:      adds r1, r6, #7
410; CHECK-NEXT: adds r1, #9
411; CHECK:      adds r5, r6, #7
412; CHECK-NEXT: adds r5, #5
413; CHECK:      bl   u
414; Stack restored before next call
415; CHECK:      add  sp, #508
416; CHECK-NEXT: add  sp, #508
417; CHECK-NEXT: add  sp, #8
418; CHECK:      bl   u
419