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 4 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. 10 // 11 // Address emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr, 12 // QualType ValueTy, bool IsIndirect, 13 // TypeInfoChars ValueInfo, CharUnits SlotSizeAndAlign, 14 // bool AllowHigherAlign, bool ForceRightAdjust = 15 // false); 16 // 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 32 // 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. 36 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]] 47 // 48 int raw_int(__builtin_va_list list) { return __builtin_va_arg(list, int); } 49 50 typedef struct { 51 int x; 52 } one_int_t; 53 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]] 66 // 67 one_int_t one_int(__builtin_va_list list) { 68 return __builtin_va_arg(list, one_int_t); 69 } 70 71 typedef struct { 72 int x; 73 int y; 74 } two_int_t; 75 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 86 // 87 two_int_t two_int(__builtin_va_list list) { 88 return __builtin_va_arg(list, two_int_t); 89 } 90 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]] 103 // 104 double raw_double(__builtin_va_list list) { 105 return __builtin_va_arg(list, double); 106 } 107 108 typedef struct { 109 double x; 110 } one_double_t; 111 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]] 126 // 127 one_double_t one_double(__builtin_va_list list) { 128 return __builtin_va_arg(list, one_double_t); 129 } 130 131 typedef struct { 132 double x; 133 double y; 134 } two_double_t; 135 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 146 // 147 two_double_t two_double(__builtin_va_list list) { 148 return __builtin_va_arg(list, two_double_t); 149 } 150 151 // Scalar smaller than the slot size (C would promote a short to int) 152 typedef struct { 153 char x; 154 } one_char_t; 155 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]] 168 // 169 one_char_t one_char(__builtin_va_list list) { 170 return __builtin_va_arg(list, one_char_t); 171 } 172 173 typedef struct { 174 short x; 175 } one_short_t; 176 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]] 189 // 190 one_short_t one_short(__builtin_va_list list) { 191 return __builtin_va_arg(list, one_short_t); 192 } 193 194 // Composite smaller than the slot size 195 typedef struct { 196 _Alignas(2) char x; 197 char y; 198 } char_pair_t; 199 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 210 // 211 char_pair_t char_pair(__builtin_va_list list) { 212 return __builtin_va_arg(list, char_pair_t); 213 } 214 215 // Empty struct 216 typedef struct { 217 } empty_t; 218 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 229 // 230 empty_t empty(__builtin_va_list list) { 231 return __builtin_va_arg(list, empty_t); 232 } 233 234 typedef struct { 235 empty_t x; 236 int y; 237 } empty_int_t; 238 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]] 251 // 252 empty_int_t empty_int(__builtin_va_list list) { 253 return __builtin_va_arg(list, empty_int_t); 254 } 255 256 typedef struct { 257 int x; 258 empty_t y; 259 } int_empty_t; 260 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]] 273 // 274 int_empty_t int_empty(__builtin_va_list list) { 275 return __builtin_va_arg(list, int_empty_t); 276 } 277 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. 281 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 311 // 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); 316 } 317 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 356 // 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); 363 } 364 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 402 // 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); 409 } 410 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; 416 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 430 // 431 void large_value(__builtin_va_list list, large_value_t *out) { 432 *out = __builtin_va_arg(list, large_value_t); 433 } 434 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 451 // 452 void vector(__builtin_va_list list, v128_t *out) { 453 *out = __builtin_va_arg(list, v128_t); 454 } 455 456 typedef struct BF { 457 float not_an_i32[2]; 458 int A : 1; 459 char B; 460 int C : 13; 461 } BF; 462 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 476 // 477 void bitfield(__builtin_va_list list, BF *out) { 478 *out = __builtin_va_arg(list, BF); 479 } 480