// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py // REQUIRES: webassembly-registered-target // RUN: %clang_cc1 -triple wasm32-unknown-unknown -emit-llvm -o - %s | FileCheck %s // Multiple targets use emitVoidPtrVAArg to lower va_arg instructions in clang // PPC is complicated, excluding from this case analysis // ForceRightAdjust is false for all non-PPC targets // AllowHigherAlign is only false for two Microsoft targets, both of which // pass most things by reference. // // Address emitVoidPtrVAArg(CodeGenFunction &CGF, Address VAListAddr, // QualType ValueTy, bool IsIndirect, // TypeInfoChars ValueInfo, CharUnits SlotSizeAndAlign, // bool AllowHigherAlign, bool ForceRightAdjust = // false); // // Target IsIndirect SlotSize AllowHigher ForceRightAdjust // ARC false four true false // ARM varies four true false // Mips false 4 or 8 true false // RISCV varies register true false // PPC elided // LoongArch varies register true false // NVPTX WIP // AMDGPU WIP // X86_32 false four true false // X86_64 MS varies eight false false // CSKY false four true false // Webassembly varies four true false // AArch64 false eight true false // AArch64 MS false eight false false // // Webassembly passes indirectly iff it's an aggregate of multiple values // Choosing this as a representative architecture to check IR generation // partly because it has a relatively simple variadic calling convention. // Int, by itself and packed in structs // CHECK-LABEL: @raw_int( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: ret i32 [[TMP0]] // int raw_int(__builtin_va_list list) { return __builtin_va_arg(list, int); } typedef struct { int x; } one_int_t; // CHECK-LABEL: @one_int( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_INT_T:%.*]], align 4 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_INT_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4 // CHECK-NEXT: ret i32 [[TMP0]] // one_int_t one_int(__builtin_va_list list) { return __builtin_va_arg(list, one_int_t); } typedef struct { int x; int y; } two_int_t; // CHECK-LABEL: @two_int( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[AGG_RESULT:%.*]], ptr align 4 [[TMP0]], i32 8, i1 false) // CHECK-NEXT: ret void // two_int_t two_int(__builtin_va_list list) { return __builtin_va_arg(list, two_int_t); } // Double, by itself and packed in structs // CHECK-LABEL: @raw_double( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8) // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 // CHECK-NEXT: ret double [[TMP1]] // double raw_double(__builtin_va_list list) { return __builtin_va_arg(list, double); } typedef struct { double x; } one_double_t; // CHECK-LABEL: @one_double( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_DOUBLE_T:%.*]], align 8 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8) // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[RETVAL]], ptr align 8 [[ARGP_CUR_ALIGNED]], i32 8, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_DOUBLE_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[COERCE_DIVE]], align 8 // CHECK-NEXT: ret double [[TMP1]] // one_double_t one_double(__builtin_va_list list) { return __builtin_va_arg(list, one_double_t); } typedef struct { double x; double y; } two_double_t; // CHECK-LABEL: @two_double( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[AGG_RESULT:%.*]], ptr align 8 [[TMP0]], i32 16, i1 false) // CHECK-NEXT: ret void // two_double_t two_double(__builtin_va_list list) { return __builtin_va_arg(list, two_double_t); } // Scalar smaller than the slot size (C would promote a short to int) typedef struct { char x; } one_char_t; // CHECK-LABEL: @one_char( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_CHAR_T:%.*]], align 1 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_CHAR_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load i8, ptr [[COERCE_DIVE]], align 1 // CHECK-NEXT: ret i8 [[TMP0]] // one_char_t one_char(__builtin_va_list list) { return __builtin_va_arg(list, one_char_t); } typedef struct { short x; } one_short_t; // CHECK-LABEL: @one_short( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_ONE_SHORT_T:%.*]], align 2 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 2, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_ONE_SHORT_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load i16, ptr [[COERCE_DIVE]], align 2 // CHECK-NEXT: ret i16 [[TMP0]] // one_short_t one_short(__builtin_va_list list) { return __builtin_va_arg(list, one_short_t); } // Composite smaller than the slot size typedef struct { _Alignas(2) char x; char y; } char_pair_t; // CHECK-LABEL: @char_pair( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[AGG_RESULT:%.*]], ptr align 2 [[TMP0]], i32 2, i1 false) // CHECK-NEXT: ret void // char_pair_t char_pair(__builtin_va_list list) { return __builtin_va_arg(list, char_pair_t); } // Empty struct typedef struct { } empty_t; // CHECK-LABEL: @empty( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_T:%.*]], align 1 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 0 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 0, i1 false) // CHECK-NEXT: ret void // empty_t empty(__builtin_va_list list) { return __builtin_va_arg(list, empty_t); } typedef struct { empty_t x; int y; } empty_int_t; // CHECK-LABEL: @empty_int( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_EMPTY_INT_T:%.*]], align 4 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_EMPTY_INT_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4 // CHECK-NEXT: ret i32 [[TMP0]] // empty_int_t empty_int(__builtin_va_list list) { return __builtin_va_arg(list, empty_int_t); } typedef struct { int x; empty_t y; } int_empty_t; // CHECK-LABEL: @int_empty( // CHECK-NEXT: entry: // CHECK-NEXT: [[RETVAL:%.*]] = alloca [[STRUCT_INT_EMPTY_T:%.*]], align 4 // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[RETVAL]], ptr align 4 [[ARGP_CUR]], i32 4, i1 false) // CHECK-NEXT: [[COERCE_DIVE:%.*]] = getelementptr inbounds nuw [[STRUCT_INT_EMPTY_T]], ptr [[RETVAL]], i32 0, i32 0 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[COERCE_DIVE]], align 4 // CHECK-NEXT: ret i32 [[TMP0]] // int_empty_t int_empty(__builtin_va_list list) { return __builtin_va_arg(list, int_empty_t); } // Need multiple va_arg instructions to check the postincrement // Using types that are passed directly as the indirect handling // is independent of the alignment handling in emitVoidPtrDirectVAArg. // CHECK-LABEL: @multiple_int( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: store i32 [[TMP0]], ptr [[TMP1]], align 4 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4 // CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4 // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP4:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4 // CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: store i32 [[TMP4]], ptr [[TMP5]], align 4 // CHECK-NEXT: ret void // void multiple_int(__builtin_va_list list, int *out0, int *out1, int *out2) { *out0 = __builtin_va_arg(list, int); *out1 = __builtin_va_arg(list, int); *out2 = __builtin_va_arg(list, int); } // Scalars in structs are an easy way of specifying alignment from C // CHECK-LABEL: @increasing_alignment( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP0]], ptr align 4 [[ARGP_CUR]], i32 1, i1 false) // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP1]], ptr align 4 [[ARGP_CUR1]], i32 2, i1 false) // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARGP_CUR3]], align 4 // CHECK-NEXT: [[TMP3:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: store i32 [[TMP2]], ptr [[TMP3]], align 4 // CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 7 // CHECK-NEXT: [[ARGP_CUR5_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP4]], i32 -8) // CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5_ALIGNED]], i32 8 // CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP5:%.*]] = load double, ptr [[ARGP_CUR5_ALIGNED]], align 8 // CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4 // CHECK-NEXT: store double [[TMP5]], ptr [[TMP6]], align 8 // CHECK-NEXT: ret void // void increasing_alignment(__builtin_va_list list, one_char_t *out0, one_short_t *out1, int *out2, double *out3) { *out0 = __builtin_va_arg(list, one_char_t); *out1 = __builtin_va_arg(list, one_short_t); *out2 = __builtin_va_arg(list, int); *out3 = __builtin_va_arg(list, double); } // CHECK-LABEL: @decreasing_alignment( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT0_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT1_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT2_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT3_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT0:%.*]], ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT1:%.*]], ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT2:%.*]], ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT3:%.*]], ptr [[OUT3_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 7 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -8) // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 8 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load double, ptr [[ARGP_CUR_ALIGNED]], align 8 // CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT0_ADDR]], align 4 // CHECK-NEXT: store double [[TMP1]], ptr [[TMP2]], align 8 // CHECK-NEXT: [[ARGP_CUR1:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT2:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR1]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT2]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr [[ARGP_CUR1]], align 4 // CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[OUT1_ADDR]], align 4 // CHECK-NEXT: store i32 [[TMP3]], ptr [[TMP4]], align 4 // CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[OUT2_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR3:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT4:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR3]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT4]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 2 [[TMP5]], ptr align 4 [[ARGP_CUR3]], i32 2, i1 false) // CHECK-NEXT: [[TMP6:%.*]] = load ptr, ptr [[OUT3_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR5:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT6:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR5]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT6]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 1 [[TMP6]], ptr align 4 [[ARGP_CUR5]], i32 1, i1 false) // CHECK-NEXT: ret void // void decreasing_alignment(__builtin_va_list list, double *out0, int *out1, one_short_t *out2, one_char_t *out3) { *out0 = __builtin_va_arg(list, double); *out1 = __builtin_va_arg(list, int); *out2 = __builtin_va_arg(list, one_short_t); *out3 = __builtin_va_arg(list, one_char_t); } // Typical edge cases, none hit special handling in VAArg lowering. typedef struct { int x[16]; double y[8]; } large_value_t; // CHECK-LABEL: @large_value( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 8 [[TMP0]], ptr align 8 [[TMP1]], i32 128, i1 false) // CHECK-NEXT: ret void // void large_value(__builtin_va_list list, large_value_t *out) { *out = __builtin_va_arg(list, large_value_t); } typedef int v128_t __attribute__((__vector_size__(16), __aligned__(16))); // CHECK-LABEL: @vector( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 15 // CHECK-NEXT: [[ARGP_CUR_ALIGNED:%.*]] = call ptr @llvm.ptrmask.p0.i32(ptr [[TMP0]], i32 -16) // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR_ALIGNED]], i32 16 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[ARGP_CUR_ALIGNED]], align 16 // CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: store <4 x i32> [[TMP1]], ptr [[TMP2]], align 16 // CHECK-NEXT: ret void // void vector(__builtin_va_list list, v128_t *out) { *out = __builtin_va_arg(list, v128_t); } typedef struct BF { float not_an_i32[2]; int A : 1; char B; int C : 13; } BF; // CHECK-LABEL: @bitfield( // CHECK-NEXT: entry: // CHECK-NEXT: [[LIST_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: [[OUT_ADDR:%.*]] = alloca ptr, align 4 // CHECK-NEXT: store ptr [[LIST:%.*]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: store ptr [[OUT:%.*]], ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OUT_ADDR]], align 4 // CHECK-NEXT: [[ARGP_CUR:%.*]] = load ptr, ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[ARGP_NEXT:%.*]] = getelementptr inbounds i8, ptr [[ARGP_CUR]], i32 4 // CHECK-NEXT: store ptr [[ARGP_NEXT]], ptr [[LIST_ADDR]], align 4 // CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARGP_CUR]], align 4 // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[TMP0]], ptr align 4 [[TMP1]], i32 12, i1 false) // CHECK-NEXT: ret void // void bitfield(__builtin_va_list list, BF *out) { *out = __builtin_va_arg(list, BF); }