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 3 4 #include <stdarg.h> 5 6 // Obviously there's more than one way to implement va_arg. This test should at 7 // least prevent unintentional regressions caused by refactoring. 8 9 va_list the_list; 10 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]] 19 } 20 21 struct bigstruct { 22 int a[10]; 23 }; 24 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) 32 // CHECK: ret void 33 } 34 35 struct aligned_bigstruct { 36 float a; 37 long double b; 38 }; 39 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) 49 // CHECK: ret void 50 } 51 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]] 62 } 63 64 struct hfa { 65 float a, b; 66 }; 67 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) 75 // CHECK: ret void 76 } 77 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. 81 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]] 91 } 92 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]] 102 } 103 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]] 115 } 116 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]] 128 } 129 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 134 // means is: 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. 141 142 typedef struct __attribute__((packed,aligned(2))) { 143 int val; 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]] 156 } 157 158 typedef struct __attribute__((aligned(16))) { 159 int val; 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) 168 // CHECK: ret void 169 } 170 171 typedef struct __attribute__((packed,aligned(2))) { 172 long long val; 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) 181 // CHECK: ret void 182 } 183 184 typedef struct __attribute__((aligned(16))) { 185 long long val; 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) 196 // CHECK: ret void 197 } 198 199 // Overaligning or underaligning a struct member changes both its alignment and 200 // size when passed as an argument. 201 202 typedef struct { 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]] 216 } 217 218 typedef struct { 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) 230 // CHECK: ret void 231 } 232 233 typedef struct { 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) 243 // CHECK: ret void 244 } 245 246 typedef struct { 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) 258 // CHECK: ret void 259 } 260 261 void check_start(int n, ...) { 262 // CHECK-LABEL: define{{.*}} void @check_start(i32 noundef %n, ...) 263 264 va_list the_list; 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]]) 268 } 269