1; RUN: llc < %s -mtriple=aarch64-pc-win32 | FileCheck %s
2
3define void @pass_va(i32 %count, ...) nounwind {
4entry:
5; CHECK: str     x30, [sp, #-80]!
6; CHECK: add     x8, sp, #24
7; CHECK: add     x0, sp, #24
8; CHECK: stp     x1, x2, [sp, #24]
9; CHECK: stp     x3, x4, [sp, #40]
10; CHECK: stp     x5, x6, [sp, #56]
11; CHECK: str     x7, [sp, #72]
12; CHECK: str     x8, [sp, #8]
13; CHECK: bl      other_func
14; CHECK: ldr     x30, [sp], #80
15; CHECK: ret
16  %ap = alloca i8*, align 8
17  %ap1 = bitcast i8** %ap to i8*
18  call void @llvm.va_start(i8* %ap1)
19  %ap2 = load i8*, i8** %ap, align 8
20  call void @other_func(i8* %ap2)
21  ret void
22}
23
24declare void @other_func(i8*) local_unnamed_addr
25
26declare void @llvm.va_start(i8*) nounwind
27declare void @llvm.va_copy(i8*, i8*) nounwind
28
29; CHECK-LABEL: f9:
30; CHECK: sub     sp, sp, #16
31; CHECK: add     x8, sp, #24
32; CHECK: add     x0, sp, #24
33; CHECK: str     x8, [sp, #8]
34; CHECK: add     sp, sp, #16
35; CHECK: ret
36define i8* @f9(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, i64 %a8, ...) nounwind {
37entry:
38  %ap = alloca i8*, align 8
39  %ap1 = bitcast i8** %ap to i8*
40  call void @llvm.va_start(i8* %ap1)
41  %ap2 = load i8*, i8** %ap, align 8
42  ret i8* %ap2
43}
44
45; CHECK-LABEL: f8:
46; CHECK: sub     sp, sp, #16
47; CHECK: add     x8, sp, #16
48; CHECK: add     x0, sp, #16
49; CHECK: str     x8, [sp, #8]
50; CHECK: add     sp, sp, #16
51; CHECK: ret
52define i8* @f8(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, i64 %a7, ...) nounwind {
53entry:
54  %ap = alloca i8*, align 8
55  %ap1 = bitcast i8** %ap to i8*
56  call void @llvm.va_start(i8* %ap1)
57  %ap2 = load i8*, i8** %ap, align 8
58  ret i8* %ap2
59}
60
61; CHECK-LABEL: f7:
62; CHECK: sub     sp, sp, #32
63; CHECK: add     x8, sp, #24
64; CHECK: str     x7, [sp, #24]
65; CHECK: add     x0, sp, #24
66; CHECK: str     x8, [sp, #8]
67; CHECK: add     sp, sp, #32
68; CHECK: ret
69define i8* @f7(i64 %a0, i64 %a1, i64 %a2, i64 %a3, i64 %a4, i64 %a5, i64 %a6, ...) nounwind {
70entry:
71  %ap = alloca i8*, align 8
72  %ap1 = bitcast i8** %ap to i8*
73  call void @llvm.va_start(i8* %ap1)
74  %ap2 = load i8*, i8** %ap, align 8
75  ret i8* %ap2
76}
77
78; CHECK-LABEL: copy1:
79; CHECK: sub     sp, sp, #80
80; CHECK: add     x8, sp, #24
81; CHECK: stp     x1, x2, [sp, #24]
82; CHECK: stp     x3, x4, [sp, #40]
83; CHECK: stp     x5, x6, [sp, #56]
84; CHECK: str     x7, [sp, #72]
85; CHECK: stp     x8, x8, [sp], #80
86; CHECK: ret
87define void @copy1(i64 %a0, ...) nounwind {
88entry:
89  %ap = alloca i8*, align 8
90  %cp = alloca i8*, align 8
91  %ap1 = bitcast i8** %ap to i8*
92  %cp1 = bitcast i8** %cp to i8*
93  call void @llvm.va_start(i8* %ap1)
94  call void @llvm.va_copy(i8* %cp1, i8* %ap1)
95  ret void
96}
97
98declare void @llvm.va_end(i8*)
99declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
100declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
101
102declare i32 @__stdio_common_vsprintf(i64, i8*, i64, i8*, i8*, i8*) local_unnamed_addr #3
103declare i64* @__local_stdio_printf_options() local_unnamed_addr #4
104
105; CHECK-LABEL: fp
106; CHECK: stp     x29, x30, [sp, #-96]
107; CHECK: str     x21,      [sp, #16]
108; CHECK: stp     x19, x20, [sp, #32]
109; CHECK: mov     x29, sp
110; CHECK: add     x8, x29, #56
111; CHECK: mov     x19, x2
112; CHECK: mov     x20, x1
113; CHECK: mov     x21, x0
114; CHECK: stp     x3, x4, [x29, #56]
115; CHECK: stp     x5, x6, [x29, #72]
116; CHECK: str     x7, [x29, #88]
117; CHECK: str     x8, [x29, #24]
118; CHECK: bl      __local_stdio_printf_options
119; CHECK: ldr     x8, [x0]
120; CHECK: add     x5, x29, #56
121; CHECK: mov     x1, x21
122; CHECK: mov     x2, x20
123; CHECK: orr     x0, x8, #0x2
124; CHECK: mov     x3, x19
125; CHECK: mov     x4, xzr
126; CHECK: bl      __stdio_common_vsprintf
127; CHECK: cmp     w0, #0
128; CHECK: csinv   w0, w0, wzr, ge
129; CHECK: ldp     x19, x20, [sp, #32]
130; CHECK: ldr     x21,      [sp, #16]
131; CHECK: ldp     x29, x30, [sp], #96
132; CHECK: ret
133define i32 @fp(i8*, i64, i8*, ...) local_unnamed_addr #6 {
134  %4 = alloca i8*, align 8
135  %5 = bitcast i8** %4 to i8*
136  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %5) #2
137  call void @llvm.va_start(i8* nonnull %5)
138  %6 = load i8*, i8** %4, align 8
139  %7 = call i64* @__local_stdio_printf_options() #2
140  %8 = load i64, i64* %7, align 8
141  %9 = or i64 %8, 2
142  %10 = call i32 @__stdio_common_vsprintf(i64 %9, i8* %0, i64 %1, i8* %2, i8* null, i8* %6) #2
143  %11 = icmp sgt i32 %10, -1
144  %12 = select i1 %11, i32 %10, i32 -1
145  call void @llvm.va_end(i8* nonnull %5)
146  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %5) #2
147  ret i32 %12
148}
149
150attributes #6 = { "frame-pointer"="all" }
151
152; CHECK-LABEL: vla
153; CHECK: stp     x29, x30, [sp, #-112]!
154; CHECK: str     x23, [sp, #16]
155; CHECK: stp     x21, x22, [sp, #32]
156; CHECK: stp     x19, x20, [sp, #48]
157; CHECK: mov     x29, sp
158; CHECK: add     x8, x29, #64
159; CHECK: str     x8, [x29, #24]
160; CHECK: mov     w8, w0
161; CHECK: add     x8, x8, #15
162; CHECK: lsr     x15, x8, #4
163; CHECK: mov     x19, x1
164; CHECK: mov     [[REG2:x[0-9]+]], sp
165; CHECK: stp     x2, x3, [x29, #64]
166; CHECK: stp     x4, x5, [x29, #80]
167; CHECK: stp     x6, x7, [x29, #96]
168; CHECK: bl      __chkstk
169; CHECK: mov     x8, sp
170; CHECK: sub     [[REG:x[0-9]+]], x8, x15, lsl #4
171; CHECK: mov     sp, [[REG]]
172; CHECK: ldr     [[REG3:x[0-9]+]], [x29, #24]
173; CHECK: sxtw    [[REG4:x[0-9]+]], w0
174; CHECK: bl      __local_stdio_printf_options
175; CHECK: ldr     x8, [x0]
176; CHECK: mov     x1, [[REG]]
177; CHECK: mov     x2, [[REG4]]
178; CHECK: mov     x3, x19
179; CHECK: orr     x0, x8, #0x2
180; CHECK: mov     x4, xzr
181; CHECK: mov     x5, [[REG3]]
182; CHECK: bl      __stdio_common_vsprintf
183; CHECK: mov     sp, [[REG2]]
184; CHECK: mov     sp, x29
185; CHECK: ldp     x19, x20, [sp, #48]
186; CHECK: ldp     x21, x22, [sp, #32]
187; CHECK: ldr     x23, [sp, #16]
188; CHECK: ldp     x29, x30, [sp], #112
189; CHECK: ret
190define void @vla(i32, i8*, ...) local_unnamed_addr {
191  %3 = alloca i8*, align 8
192  %4 = bitcast i8** %3 to i8*
193  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %4) #5
194  call void @llvm.va_start(i8* nonnull %4)
195  %5 = zext i32 %0 to i64
196  %6 = call i8* @llvm.stacksave()
197  %7 = alloca i8, i64 %5, align 1
198  %8 = load i8*, i8** %3, align 8
199  %9 = sext i32 %0 to i64
200  %10 = call i64* @__local_stdio_printf_options()
201  %11 = load i64, i64* %10, align 8
202  %12 = or i64 %11, 2
203  %13 = call i32 @__stdio_common_vsprintf(i64 %12, i8* nonnull %7, i64 %9, i8* %1, i8* null, i8* %8)
204  call void @llvm.va_end(i8* nonnull %4)
205  call void @llvm.stackrestore(i8* %6)
206  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %4) #5
207  ret void
208}
209
210declare i8* @llvm.stacksave()
211declare void @llvm.stackrestore(i8*)
212
213; CHECK-LABEL: snprintf
214; CHECK-DAG: sub     sp,  sp, #96
215; CHECK-DAG: str     x30, [sp, #16]
216; CHECK-DAG: str     x21, [sp, #24]
217; CHECK-DAG: stp     x19, x20, [sp, #32]
218; CHECK-DAG: add     x8, sp, #56
219; CHECK-DAG: mov     x19, x2
220; CHECK-DAG: mov     x20, x1
221; CHECK-DAG: mov     x21, x0
222; CHECK-DAG: stp     x3, x4, [sp, #56]
223; CHECK-DAG: stp     x5, x6, [sp, #72]
224; CHECK-DAG: str     x7, [sp, #88]
225; CHECK-DAG: str     x8, [sp, #8]
226; CHECK-DAG: bl      __local_stdio_printf_options
227; CHECK-DAG: ldr     x8, [x0]
228; CHECK-DAG: add     x5, sp, #56
229; CHECK-DAG: mov     x1, x21
230; CHECK-DAG: mov     x2, x20
231; CHECK-DAG: orr     x0, x8, #0x2
232; CHECK-DAG: mov     x3, x19
233; CHECK-DAG: mov     x4, xzr
234; CHECK-DAG: bl      __stdio_common_vsprintf
235; CHECK-DAG: ldr     x30, [sp, #16]
236; CHECK-DAG: ldr     x21, [sp, #24]
237; CHECK-DAG: ldp     x19, x20, [sp, #32]
238; CHECK-DAG: cmp     w0, #0
239; CHECK-DAG: csinv   w0, w0, wzr, ge
240; CHECK-DAG: add     sp, sp, #96
241; CHECK-DAG: ret
242define i32 @snprintf(i8*, i64, i8*, ...) local_unnamed_addr #5 {
243  %4 = alloca i8*, align 8
244  %5 = bitcast i8** %4 to i8*
245  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %5) #2
246  call void @llvm.va_start(i8* nonnull %5)
247  %6 = load i8*, i8** %4, align 8
248  %7 = call i64* @__local_stdio_printf_options() #2
249  %8 = load i64, i64* %7, align 8
250  %9 = or i64 %8, 2
251  %10 = call i32 @__stdio_common_vsprintf(i64 %9, i8* %0, i64 %1, i8* %2, i8* null, i8* %6) #2
252  %11 = icmp sgt i32 %10, -1
253  %12 = select i1 %11, i32 %10, i32 -1
254  call void @llvm.va_end(i8* nonnull %5)
255  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %5) #2
256  ret i32 %12
257}
258
259; CHECK-LABEL: fixed_params
260; CHECK: sub     sp,  sp, #32
261; CHECK-DAG: mov     w6,  w3
262; CHECK-DAG: mov     [[REG1:w[0-9]+]],  w2
263; CHECK: mov     w2, w1
264; CHECK: fmov    x1,  d0
265; CHECK: fmov    x3,  d1
266; CHECK: fmov    x5,  d2
267; CHECK: fmov    x7,  d3
268; CHECK: str     w4,  [sp]
269; CHECK: mov     w4,  [[REG1]]
270; CHECK: str     x30, [sp, #16]
271; CHECK: str     d4,  [sp, #8]
272; CHECK: bl      varargs
273; CHECK: ldr     x30, [sp, #16]
274; CHECK: add     sp,  sp, #32
275; CHECK: ret
276define void @fixed_params(i32, double, i32, double, i32, double, i32, double, i32, double) nounwind {
277  tail call void (i32, ...) @varargs(i32 %0, double %1, i32 %2, double %3, i32 %4, double %5, i32 %6, double %7, i32 %8, double %9)
278  ret void
279}
280
281declare void @varargs(i32, ...) local_unnamed_addr
282