[LLVM][IR] Use splat syntax when printing ConstantExpr based splats. (#116856)
[llvm-project.git] / clang / test / CodeGen / voidptr-vaarg.c
bloba0211642bd82ffc38ca719fea67f27bdb38b049c
1 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
2 // REQUIRES: webassembly-registered-target
3 // RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -o - %s | FileCheck %s
5 // Multiple targets use emitVoidPtrVAArg to lower va_arg instructions in clang
6 // PPC is complicated, excluding from this case analysis
7 // ForceRightAdjust is false for all non-PPC targets
8 // AllowHigherAlign is only false for two Microsoft targets, both of which
9 // pass most things by reference.
11 // Address emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr,
12 // QualType ValueTy, bool IsIndirect,
13 // TypeInfoChars ValueInfo, CharUnits SlotSizeAndAlign,
14 // bool AllowHigherAlign, bool ForceRightAdjust =
15 // false);
17 // Target IsIndirect SlotSize AllowHigher ForceRightAdjust
18 // ARC false four true false
19 // ARM varies four true false
20 // Mips false 4 or 8 true false
21 // RISCV varies register true false
22 // PPC elided
23 // LoongArch varies register true false
24 // NVPTX WIP
25 // AMDGPU WIP
26 // X86_32 false four true false
27 // X86_64 MS varies eight false false
28 // CSKY false four true false
29 // Webassembly varies four true false
30 // AArch64 false eight true false
31 // AArch64 MS false eight false false
33 // Webassembly passes indirectly iff it's an aggregate of multiple values
34 // Choosing this as a representative architecture to check IR generation
35 // partly because it has a relatively simple variadic calling convention.
37 // Int, by itself and packed in structs
38 // CHECK-LABEL: @raw_int(
39 // CHECK-NEXT: entry:
40 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
41 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
42 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
43 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
44 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
45 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4
46 // CHECK-NEXT: ret i32 [[TMP0]]
48 int raw_int(__builtin_va_list list) { return __builtin_va_arg(list, int); }
50 typedef struct {
51 int x;
52 } one_int_t;
54 // CHECK-LABEL: @one_int(
55 // CHECK-NEXT: entry:
56 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_INT_T:%.*]], align 4
57 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
58 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
59 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
60 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
61 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
62 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
63 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_INT_T]], ptr [[RETVAL]], i32 0, i32 0
64 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
65 // CHECK-NEXT: ret i32 [[TMP0]]
67 one_int_t one_int(__builtin_va_list list) {
68 return __builtin_va_arg(list, one_int_t);
71 typedef struct {
72 int x;
73 int y;
74 } two_int_t;
76 // CHECK-LABEL: @two_int(
77 // CHECK-NEXT: entry:
78 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
79 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
80 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
81 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
82 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
83 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
84 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT:%.*]], ptr align 4 [[TMP0]], i32 8, i1 false)
85 // CHECK-NEXT: ret void
87 two_int_t two_int(__builtin_va_list list) {
88 return __builtin_va_arg(list, two_int_t);
91 // Double, by itself and packed in structs
92 // CHECK-LABEL: @raw_double(
93 // CHECK-NEXT: entry:
94 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
95 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
96 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
97 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
98 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
99 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
100 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
101 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8
102 // CHECK-NEXT: ret double [[TMP1]]
104 double raw_double(__builtin_va_list list) {
105 return __builtin_va_arg(list, double);
108 typedef struct {
109 double x;
110 } one_double_t;
112 // CHECK-LABEL: @one_double(
113 // CHECK-NEXT: entry:
114 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_DOUBLE_T:%.*]], align 8
115 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
116 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
117 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
118 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
119 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
120 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
121 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
122 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[RETVAL]], ptr align 8 [[ARGP_CUR_ALIGNED]], i32 8, i1 false)
123 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_DOUBLE_T]], ptr [[RETVAL]], i32 0, i32 0
124 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[COERCE_DIVE]], align 8
125 // CHECK-NEXT: ret double [[TMP1]]
127 one_double_t one_double(__builtin_va_list list) {
128 return __builtin_va_arg(list, one_double_t);
131 typedef struct {
132 double x;
133 double y;
134 } two_double_t;
136 // CHECK-LABEL: @two_double(
137 // CHECK-NEXT: entry:
138 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
139 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
140 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
141 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
142 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
143 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
144 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[TMP0]], i32 16, i1 false)
145 // CHECK-NEXT: ret void
147 two_double_t two_double(__builtin_va_list list) {
148 return __builtin_va_arg(list, two_double_t);
151 // Scalar smaller than the slot size (C would promote a short to int)
152 typedef struct {
153 char x;
154 } one_char_t;
156 // CHECK-LABEL: @one_char(
157 // CHECK-NEXT: entry:
158 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_CHAR_T:%.*]], align 1
159 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
160 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
161 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
162 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
163 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
164 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false)
165 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_CHAR_T]], ptr [[RETVAL]], i32 0, i32 0
166 // CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[COERCE_DIVE]], align 1
167 // CHECK-NEXT: ret i8 [[TMP0]]
169 one_char_t one_char(__builtin_va_list list) {
170 return __builtin_va_arg(list, one_char_t);
173 typedef struct {
174 short x;
175 } one_short_t;
177 // CHECK-LABEL: @one_short(
178 // CHECK-NEXT: entry:
179 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_SHORT_T:%.*]], align 2
180 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
181 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
182 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
183 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
184 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
185 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 2, i1 false)
186 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_SHORT_T]], ptr [[RETVAL]], i32 0, i32 0
187 // CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[COERCE_DIVE]], align 2
188 // CHECK-NEXT: ret i16 [[TMP0]]
190 one_short_t one_short(__builtin_va_list list) {
191 return __builtin_va_arg(list, one_short_t);
194 // Composite smaller than the slot size
195 typedef struct {
196 _Alignas(2) char x;
197 char y;
198 } char_pair_t;
200 // CHECK-LABEL: @char_pair(
201 // CHECK-NEXT: entry:
202 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
203 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
204 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
205 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
206 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
207 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
208 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[AGG_RESULT:%.*]], ptr align 2 [[TMP0]], i32 2, i1 false)
209 // CHECK-NEXT: ret void
211 char_pair_t char_pair(__builtin_va_list list) {
212 return __builtin_va_arg(list, char_pair_t);
215 // Empty struct
216 typedef struct {
217 } empty_t;
219 // CHECK-LABEL: @empty(
220 // CHECK-NEXT: entry:
221 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_T:%.*]], align 1
222 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
223 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
224 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
225 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 0
226 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
227 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 0, i1 false)
228 // CHECK-NEXT: ret void
230 empty_t empty(__builtin_va_list list) {
231 return __builtin_va_arg(list, empty_t);
234 typedef struct {
235 empty_t x;
236 int y;
237 } empty_int_t;
239 // CHECK-LABEL: @empty_int(
240 // CHECK-NEXT: entry:
241 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_INT_T:%.*]], align 4
242 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
243 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
244 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
245 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
246 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
247 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
248 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_EMPTY_INT_T]], ptr [[RETVAL]], i32 0, i32 0
249 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
250 // CHECK-NEXT: ret i32 [[TMP0]]
252 empty_int_t empty_int(__builtin_va_list list) {
253 return __builtin_va_arg(list, empty_int_t);
256 typedef struct {
257 int x;
258 empty_t y;
259 } int_empty_t;
261 // CHECK-LABEL: @int_empty(
262 // CHECK-NEXT: entry:
263 // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_INT_EMPTY_T:%.*]], align 4
264 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
265 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
266 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
267 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
268 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
269 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false)
270 // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_INT_EMPTY_T]], ptr [[RETVAL]], i32 0, i32 0
271 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4
272 // CHECK-NEXT: ret i32 [[TMP0]]
274 int_empty_t int_empty(__builtin_va_list list) {
275 return __builtin_va_arg(list, int_empty_t);
278 // Need multiple va_arg instructions to check the postincrement
279 // Using types that are passed directly as the indirect handling
280 // is independent of the alignment handling in emitVoidPtrDirectVAArg.
282 // CHECK-LABEL: @multiple_int(
283 // CHECK-NEXT: entry:
284 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
285 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
286 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
287 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
288 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
289 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
290 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
291 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
292 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
293 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
294 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
295 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4
296 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
297 // CHECK-NEXT: store i32 [[TMP0]], ptr [[TMP1]], align 4
298 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
299 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
300 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
301 // CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4
302 // CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
303 // CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4
304 // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
305 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
306 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
307 // CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4
308 // CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
309 // CHECK-NEXT: store i32 [[TMP4]], ptr [[TMP5]], align 4
310 // CHECK-NEXT: ret void
312 void multiple_int(__builtin_va_list list, int *out0, int *out1, int *out2) {
313 *out0 = __builtin_va_arg(list, int);
314 *out1 = __builtin_va_arg(list, int);
315 *out2 = __builtin_va_arg(list, int);
318 // Scalars in structs are an easy way of specifying alignment from C
319 // CHECK-LABEL: @increasing_alignment(
320 // CHECK-NEXT: entry:
321 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
322 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
323 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
324 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
325 // CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4
326 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
327 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
328 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
329 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
330 // CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4
331 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
332 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
333 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
334 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
335 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP0]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false)
336 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
337 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
338 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
339 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
340 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP1]], ptr align 4 [[ARGP_CUR1]], i32 2, i1 false)
341 // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
342 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
343 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
344 // CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4
345 // CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
346 // CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4
347 // CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
348 // CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 7
349 // CHECK-NEXT: [[ARGP_CUR5_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP4]], i32 -8)
350 // CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5_ALIGNED]], i32 8
351 // CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4
352 // CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[ARGP_CUR5_ALIGNED]], align 8
353 // CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4
354 // CHECK-NEXT: store double [[TMP5]], ptr [[TMP6]], align 8
355 // CHECK-NEXT: ret void
357 void increasing_alignment(__builtin_va_list list, one_char_t *out0,
358 one_short_t *out1, int *out2, double *out3) {
359 *out0 = __builtin_va_arg(list, one_char_t);
360 *out1 = __builtin_va_arg(list, one_short_t);
361 *out2 = __builtin_va_arg(list, int);
362 *out3 = __builtin_va_arg(list, double);
365 // CHECK-LABEL: @decreasing_alignment(
366 // CHECK-NEXT: entry:
367 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
368 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4
369 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4
370 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4
371 // CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4
372 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
373 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4
374 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4
375 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4
376 // CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4
377 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
378 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7
379 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8)
380 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8
381 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
382 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8
383 // CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4
384 // CHECK-NEXT: store double [[TMP1]], ptr [[TMP2]], align 8
385 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
386 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4
387 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4
388 // CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4
389 // CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4
390 // CHECK-NEXT: store i32 [[TMP3]], ptr [[TMP4]], align 4
391 // CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4
392 // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
393 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4
394 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4
395 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP5]], ptr align 4 [[ARGP_CUR3]], i32 2, i1 false)
396 // CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4
397 // CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
398 // CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 4
399 // CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4
400 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP6]], ptr align 4 [[ARGP_CUR5]], i32 1, i1 false)
401 // CHECK-NEXT: ret void
403 void decreasing_alignment(__builtin_va_list list, double *out0, int *out1,
404 one_short_t *out2, one_char_t *out3) {
405 *out0 = __builtin_va_arg(list, double);
406 *out1 = __builtin_va_arg(list, int);
407 *out2 = __builtin_va_arg(list, one_short_t);
408 *out3 = __builtin_va_arg(list, one_char_t);
411 // Typical edge cases, none hit special handling in VAArg lowering.
412 typedef struct {
413 int x[16];
414 double y[8];
415 } large_value_t;
417 // CHECK-LABEL: @large_value(
418 // CHECK-NEXT: entry:
419 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
420 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
421 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
422 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
423 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
424 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
425 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
426 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
427 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
428 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[TMP0]], ptr align 8 [[TMP1]], i32 128, i1 false)
429 // CHECK-NEXT: ret void
431 void large_value(__builtin_va_list list, large_value_t *out) {
432 *out = __builtin_va_arg(list, large_value_t);
435 typedef int v128_t __attribute__((__vector_size__(16), __aligned__(16)));
436 // CHECK-LABEL: @vector(
437 // CHECK-NEXT: entry:
438 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
439 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
440 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
441 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
442 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
443 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 15
444 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16)
445 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 16
446 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
447 // CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[ARGP_CUR_ALIGNED]], align 16
448 // CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
449 // CHECK-NEXT: store <4 x i32> [[TMP1]], ptr [[TMP2]], align 16
450 // CHECK-NEXT: ret void
452 void vector(__builtin_va_list list, v128_t *out) {
453 *out = __builtin_va_arg(list, v128_t);
456 typedef struct BF {
457 float not_an_i32[2];
458 int A : 1;
459 char B;
460 int C : 13;
461 } BF;
463 // CHECK-LABEL: @bitfield(
464 // CHECK-NEXT: entry:
465 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4
466 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4
467 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4
468 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4
469 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4
470 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4
471 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4
472 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4
473 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4
474 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[TMP0]], ptr align 4 [[TMP1]], i32 12, i1 false)
475 // CHECK-NEXT: ret void
477 void bitfield(__builtin_va_list list, BF *out) {
478 *out = __builtin_va_arg(list, BF);