1 // RUN: %clang_cc1 -triple arm-none-eabi -emit-llvm -o - %s | FileCheck %s
2 // RUN: %clang_cc1 -triple armeb-none-eabi -emit-llvm -o - %s | FileCheck %s
6 // Obviously there's more than one way to implement va_arg. This test should at
7 // least prevent unintentional regressions caused by refactoring.
11 int simple_int(void) {
12 // CHECK-LABEL: define{{.*}} i32 @simple_int
13 return va_arg(the_list
, int);
14 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
15 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 4
16 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
17 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i32, ptr [[CUR]]
18 // CHECK: ret i32 [[RESULT]]
25 struct bigstruct
simple_struct(void) {
26 // CHECK-LABEL: define{{.*}} void @simple_struct(ptr dead_on_unwind noalias writable sret(%struct.bigstruct) align 4 %agg.result)
27 return va_arg(the_list
, struct bigstruct
);
28 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
29 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 40
30 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
31 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.result, ptr align 4 [[CUR]], i32 40, i1 false)
35 struct aligned_bigstruct
{
40 struct aligned_bigstruct
simple_aligned_struct(void) {
41 // CHECK-LABEL: define{{.*}} void @simple_aligned_struct(ptr dead_on_unwind noalias writable sret(%struct.aligned_bigstruct) align 8 %agg.result)
42 return va_arg(the_list
, struct aligned_bigstruct
);
43 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
44 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
45 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
46 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 16
47 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
48 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 8 %agg.result, ptr align 8 [[CUR_ALIGNED]], i32 16, i1 false)
52 double simple_double(void) {
53 // CHECK-LABEL: define{{.*}} double @simple_double
54 return va_arg(the_list
, double);
55 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
56 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
57 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
58 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 8
59 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
60 // CHECK: [[RESULT:%[a-z0-9._]+]] = load double, ptr [[CUR_ALIGNED]]
61 // CHECK: ret double [[RESULT]]
68 struct hfa
simple_hfa(void) {
69 // CHECK-LABEL: define{{.*}} void @simple_hfa(ptr dead_on_unwind noalias writable sret(%struct.hfa) align 4 %agg.result)
70 return va_arg(the_list
, struct hfa
);
71 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
72 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 8
73 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
74 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %agg.result, ptr align 4 [[CUR]], i32 8, i1 false)
78 // Over and under alignment on fundamental types has no effect on parameter
79 // passing, so the code generated for va_arg should be the same as for
80 // non-aligned fundamental types.
82 typedef int underaligned_int
__attribute__((packed
,aligned(2)));
83 underaligned_int
underaligned_int_test(void) {
84 // CHECK-LABEL: define{{.*}} i32 @underaligned_int_test()
85 return va_arg(the_list
, underaligned_int
);
86 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
87 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 4
88 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
89 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i32, ptr [[CUR]]
90 // CHECK: ret i32 [[RESULT]]
93 typedef int overaligned_int
__attribute__((aligned(32)));
94 overaligned_int
overaligned_int_test(void) {
95 // CHECK-LABEL: define{{.*}} i32 @overaligned_int_test()
96 return va_arg(the_list
, overaligned_int
);
97 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
98 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 4
99 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
100 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i32, ptr [[CUR]]
101 // CHECK: ret i32 [[RESULT]]
104 typedef long long underaligned_long_long
__attribute__((packed
,aligned(2)));
105 underaligned_long_long
underaligned_long_long_test(void) {
106 // CHECK-LABEL: define{{.*}} i64 @underaligned_long_long_test()
107 return va_arg(the_list
, underaligned_long_long
);
108 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
109 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
110 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
111 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 8
112 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
113 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i64, ptr [[CUR_ALIGNED]]
114 // CHECK: ret i64 [[RESULT]]
117 typedef long long overaligned_long_long
__attribute__((aligned(32)));
118 overaligned_long_long
overaligned_long_long_test(void) {
119 // CHECK-LABEL: define{{.*}} i64 @overaligned_long_long_test()
120 return va_arg(the_list
, overaligned_long_long
);
121 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
122 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
123 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
124 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 8
125 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
126 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i64, ptr [[CUR_ALIGNED]]
127 // CHECK: ret i64 [[RESULT]]
130 // The way that attributes applied to a struct change parameter passing is a
131 // little strange, in that the alignment due to attributes is used when
132 // calculating the size of the struct, but the alignment is based only on the
133 // alignment of the members (which can be affected by attributes). What this
135 // * The only effect of the aligned attribute on a struct is to increase its
136 // size if the alignment is greater than the member alignment.
137 // * The packed attribute is considered as applying to the members, so it will
138 // affect the alignment.
139 // Additionally the alignment can't go below 4 or above 8, so it's only
140 // long long and double that can be affected by a change in alignment.
142 typedef struct __attribute__((packed
,aligned(2))) {
144 } underaligned_int_struct
;
145 underaligned_int_struct
underaligned_int_struct_test(void) {
146 // CHECK-LABEL: define{{.*}} i32 @underaligned_int_struct_test()
147 return va_arg(the_list
, underaligned_int_struct
);
148 // CHECK: [[RETVAL:%[a-z0-9._]+]] = alloca %struct.underaligned_int_struct, align 2
149 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
150 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 4
151 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
152 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[RETVAL]], ptr align 4 [[CUR]], i32 4, i1 false)
153 // CHECK: [[COERCE:%[a-z0-9._]+]] = getelementptr inbounds nuw %struct.underaligned_int_struct, ptr [[RETVAL]], i32 0, i32 0
154 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i32, ptr [[COERCE]]
155 // CHECK: ret i32 [[RESULT]]
158 typedef struct __attribute__((aligned(16))) {
160 } overaligned_int_struct
;
161 overaligned_int_struct
overaligned_int_struct_test(void) {
162 // CHECK-LABEL: define{{.*}} void @overaligned_int_struct_test(ptr dead_on_unwind noalias writable sret(%struct.overaligned_int_struct) align 16 %agg.result)
163 return va_arg(the_list
, overaligned_int_struct
);
164 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
165 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 16
166 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
167 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 4 [[CUR]], i32 16, i1 false)
171 typedef struct __attribute__((packed
,aligned(2))) {
173 } underaligned_long_long_struct
;
174 underaligned_long_long_struct
underaligned_long_long_struct_test(void) {
175 // CHECK-LABEL: define{{.*}} void @underaligned_long_long_struct_test(ptr dead_on_unwind noalias writable sret(%struct.underaligned_long_long_struct) align 2 %agg.result)
176 return va_arg(the_list
, underaligned_long_long_struct
);
177 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
178 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 8
179 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
180 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 2 %agg.result, ptr align 4 [[CUR]], i32 8, i1 false)
184 typedef struct __attribute__((aligned(16))) {
186 } overaligned_long_long_struct
;
187 overaligned_long_long_struct
overaligned_long_long_struct_test(void) {
188 // CHECK-LABEL: define{{.*}} void @overaligned_long_long_struct_test(ptr dead_on_unwind noalias writable sret(%struct.overaligned_long_long_struct) align 16 %agg.result)
189 return va_arg(the_list
, overaligned_long_long_struct
);
190 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
191 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
192 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
193 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 16
194 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
195 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 8 [[CUR_ALIGNED]], i32 16, i1 false)
199 // Overaligning or underaligning a struct member changes both its alignment and
200 // size when passed as an argument.
203 int val
__attribute__((packed
,aligned(2)));
204 } underaligned_int_struct_member
;
205 underaligned_int_struct_member
underaligned_int_struct_member_test(void) {
206 // CHECK-LABEL: define{{.*}} i32 @underaligned_int_struct_member_test()
207 return va_arg(the_list
, underaligned_int_struct_member
);
208 // CHECK: [[RETVAL:%[a-z0-9._]+]] = alloca %struct.underaligned_int_struct_member, align 2
209 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
210 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 4
211 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
212 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[RETVAL]], ptr align 4 [[CUR]], i32 4, i1 false)
213 // CHECK: [[COERCE:%[a-z0-9._]+]] = getelementptr inbounds nuw %struct.underaligned_int_struct_member, ptr [[RETVAL]], i32 0, i32 0
214 // CHECK: [[RESULT:%[a-z0-9._]+]] = load i32, ptr [[COERCE]]
215 // CHECK: ret i32 [[RESULT]]
219 int val
__attribute__((aligned(16)));
220 } overaligned_int_struct_member
;
221 overaligned_int_struct_member
overaligned_int_struct_member_test(void) {
222 // CHECK-LABEL: define{{.*}} void @overaligned_int_struct_member_test(ptr dead_on_unwind noalias writable sret(%struct.overaligned_int_struct_member) align 16 %agg.result)
223 return va_arg(the_list
, overaligned_int_struct_member
);
224 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
225 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
226 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
227 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 16
228 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
229 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 8 [[CUR_ALIGNED]], i32 16, i1 false)
234 long long val
__attribute__((packed
,aligned(2)));
235 } underaligned_long_long_struct_member
;
236 underaligned_long_long_struct_member
underaligned_long_long_struct_member_test(void) {
237 // CHECK-LABEL: define{{.*}} void @underaligned_long_long_struct_member_test(ptr dead_on_unwind noalias writable sret(%struct.underaligned_long_long_struct_member) align 2 %agg.result)
238 return va_arg(the_list
, underaligned_long_long_struct_member
);
239 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
240 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 8
241 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
242 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 2 %agg.result, ptr align 4 [[CUR]], i32 8, i1 false)
247 long long val
__attribute__((aligned(16)));
248 } overaligned_long_long_struct_member
;
249 overaligned_long_long_struct_member
overaligned_long_long_struct_member_test(void) {
250 // CHECK-LABEL: define{{.*}} void @overaligned_long_long_struct_member_test(ptr dead_on_unwind noalias writable sret(%struct.overaligned_long_long_struct_member) align 16 %agg.result)
251 return va_arg(the_list
, overaligned_long_long_struct_member
);
252 // CHECK: [[CUR:%[a-z0-9._]+]] = load ptr, ptr @the_list, align 4
253 // CHECK: [[CUR_INT_ADD:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR]], i32 7
254 // CHECK: [[CUR_ALIGNED:%[a-z0-9._]+]] = call ptr @llvm.ptrmask.p0.i32(ptr [[CUR_INT_ADD]], i32 -8)
255 // CHECK: [[NEXT:%[a-z0-9._]+]] = getelementptr inbounds i8, ptr [[CUR_ALIGNED]], i32 16
256 // CHECK: store ptr [[NEXT]], ptr @the_list, align 4
257 // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 16 %agg.result, ptr align 8 [[CUR_ALIGNED]], i32 16, i1 false)
261 void check_start(int n
, ...) {
262 // CHECK-LABEL: define{{.*}} void @check_start(i32 noundef %n, ...)
265 va_start(the_list
, n
);
266 // CHECK: [[THE_LIST:%[a-z0-9._]+]] = alloca %struct.__va_list
267 // CHECK: call void @llvm.va_start.p0(ptr [[THE_LIST]])