llvm/clang/test/CodeGen/voidptr-vaarg.c

// 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);
}