blob: 0a31b47d8dc58f9bf211d8709e1b91a3d13e71a4 [file] [edit]
// RUN: %clang_cc1 -triple s390x-ibm-zos \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKNOVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-feature +vector \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z13 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z14 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch12 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu z15 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch13 \
// RUN: -emit-llvm -no-enable-noundef-analysis -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC
// RUN: %clang_cc1 -triple s390x-ibm-zos -target-cpu arch11 \
// RUN: -DTEST_VEC -fzvector -emit-llvm -no-enable-noundef-analysis \
// RUN: -o - %s | FileCheck %s --check-prefixes=CHECK,CHECKVEC,CHECK-ZVEC
// Scalar types
char pass_char(char arg) { return arg; }
// CHECK-LABEL: define signext i8 @pass_char(i8 signext %{{.*}})
signed char pass_schar(signed char arg) { return arg; }
// CHECK-LABEL: define signext i8 @pass_schar(i8 signext %{{.*}})
unsigned char pass_uchar(unsigned char arg) { return arg; }
// CHECK-LABEL: define zeroext i8 @pass_uchar(i8 zeroext %{{.*}})
short pass_short(short arg) { return arg; }
// CHECK-LABEL: define signext i16 @pass_short(i16 signext %{{.*}})
int pass_int(int arg) { return arg; }
// CHECK-LABEL: define signext i32 @pass_int(i32 signext %{{.*}})
long pass_long(long arg) { return arg; }
// CHECK-LABEL: define signext i64 @pass_long(i64 signext %{{.*}})
long long pass_longlong(long long arg) { return arg; }
// CHECK-LABEL: define i64 @pass_longlong(i64 %{{.*}})
float pass_float(float arg) { return arg; }
// CHECK-LABEL: define float @pass_float(float %{{.*}})
double pass_double(double arg) { return arg; }
// CHECK-LABEL: define double @pass_double(double %{{.*}})
long double pass_longdouble(long double arg) { return arg; }
// CHECK-LABEL: define fp128 @pass_longdouble(fp128 %{{.*}})
enum Color { Red, Blue };
enum Color pass_enum(enum Color arg) { return arg; }
// CHECK-LABEL: define zeroext i32 @pass_enum(i32 zeroext %{{.*}})
#ifdef TEST_VEC
vector unsigned int pass_vector(vector unsigned int arg) { return arg; };
// CHECK-ZVEC-LABEL: define <4 x i32> @pass_vector(<4 x i32> %{{.*}})
struct SingleVec { vector unsigned int v; };
struct SingleVec pass_SingleVec_agg(struct SingleVec arg) { return arg; };
// CHECK-ZVEC-LABEL: define inreg [2 x i64] @pass_SingleVec_agg([2 x i64] %{{.*}})
#endif
// Complex types
_Complex float pass_complex_float(_Complex float arg) { return arg; }
// CHECK-LABEL: define { float, float } @pass_complex_float({ float, float } %{{.*}})
_Complex double pass_complex_double(_Complex double arg) { return arg; }
// CHECK-LABEL: define { double, double } @pass_complex_double({ double, double } %{{.*}})
_Complex long double pass_complex_longdouble(_Complex long double arg) { return arg; }
// CHECK-LABEL: define { fp128, fp128 } @pass_complex_longdouble({ fp128, fp128 } %{{.*}})
// Verify that the following are complex-like types
struct complexlike_float { float re, im; };
struct complexlike_float pass_complexlike_float(struct complexlike_float arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_float @pass_complexlike_float({ float, float } %{{.*}})
struct complexlike_double { double re, im; };
struct complexlike_double pass_complexlike_double(struct complexlike_double arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_double @pass_complexlike_double({ double, double } %{{.*}})
struct complexlike_longdouble { long double re, im; };
struct complexlike_longdouble pass_complexlike_longdouble(struct complexlike_longdouble arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_longdouble @pass_complexlike_longdouble({ fp128, fp128 } %{{.*}})
struct single_element_float { float f; };
struct complexlike_struct {
struct single_element_float x;
struct single_element_float y;
};
struct complexlike_struct pass_complexlike_struct(struct complexlike_struct arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_struct @pass_complexlike_struct({ float, float } %{{.*}})
struct single_element_float_arr {
unsigned int :0;
float f[1];
};
struct complexlike_struct2 {
struct single_element_float_arr x;
struct single_element_float_arr y;
};
struct complexlike_struct2 pass_complexlike_struct2(struct complexlike_struct2 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct2(i64 %{{.*}})
struct float_and_empties {
struct S {} s;
int a[0];
float f;
};
struct complexlike_struct3 {
struct float_and_empties x;
struct float_and_empties y;
};
struct complexlike_struct3 pass_complexlike_struct3(struct complexlike_struct3 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_complexlike_struct3(i64 %{{.*}})
union two_float_union { float a; float b; };
struct complexlike_struct_with_union {
float a;
union two_float_union b;
};
struct complexlike_struct_with_union pass_complexlike_struct_with_union(struct complexlike_struct_with_union arg) { return arg; }
// CHECK-LABEL: struct.complexlike_struct_with_union @pass_complexlike_struct_with_union({ float, float } %{{.*}})
// structures with one field as complex type are not considered complex types.
struct single_complex_struct {
_Complex float f;
};
struct single_complex_struct pass_single_complex_struct(struct single_complex_struct arg) {return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_single_complex_struct(i64 %{{.*}})
// Structures with extra padding are not considered complex types.
struct complexlike_float_padded1 {
float x __attribute__((aligned(8)));
float y __attribute__((aligned(8)));
};
struct complexlike_float_padded1 pass_complexlike_float_padded1(struct complexlike_float_padded1 arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded1([2 x i64] %{{.*}})
struct complexlike_float_padded2 {
float x;
float y;
} __attribute__((aligned(16)));
struct complexlike_float_padded2 pass_complexlike_float_padded2(struct complexlike_float_padded2 arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded2([2 x i64] %{{.*}})
struct single_padded_struct {
float f;
unsigned int :2;
};
struct complexlike_float_padded3 {
struct single_padded_struct x;
struct single_padded_struct y;
};
struct complexlike_float_padded3 pass_complexlike_float_padded3(struct complexlike_float_padded3 arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_float_padded3([2 x i64] %{{.*}})
struct multi_element_float_arr { float f[2]; };
struct complexlike_struct4 {
struct multi_element_float_arr x;
struct multi_element_float_arr y;
};
struct complexlike_struct4 pass_complexlike_struct4(struct complexlike_struct4 arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_complexlike_struct4([2 x i64] %{{.*}})
typedef double align32_double __attribute__((aligned(32)));
struct complexlike_double_padded {
align32_double x;
double y;
};
struct complexlike_double_padded pass_complexlike_double_padded(struct complexlike_double_padded arg) { return arg; }
// CHECK-LABEL: define void @pass_complexlike_double_padded(ptr {{.*}} sret(%struct.complexlike_double_padded) align 32 %{{.*}}, [4 x i64] %{{.*}})
struct complexlike_double_padded2 {
__attribute__((aligned(256))) align32_double x; // attribute on both type and typedef
double y;
};
struct complexlike_double_padded2 pass_complexlike_double_padded2(struct complexlike_double_padded2 arg) { return arg; }
// CHECK-LABEL: define void @pass_complexlike_double_padded2(ptr {{.*}} sret(%struct.complexlike_double_padded2) align 256 %{{.*}}, [32 x i64] %{{.*}})
struct complexlike_double_padded3 {
__attribute__((aligned(32))) double x; // attribute on type only
double y;
};
struct complexlike_double_padded3 pass_complexlike_double_padded3(struct complexlike_double_padded3 arg) { return arg; }
// CHECK-LABEL: define void @pass_complexlike_double_padded3(ptr {{.*}} sret(%struct.complexlike_double_padded3) align 32 %{{.*}}, [4 x i64] %{{.*}})
// Aggregate types
struct agg_1byte { char a[1]; };
struct agg_1byte pass_agg_1byte(struct agg_1byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_1byte(i64 %{{.*}})
struct agg_2byte { char a[2]; };
struct agg_2byte pass_agg_2byte(struct agg_2byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_2byte(i64 %{{.*}})
struct agg_3byte { char a[3]; };
struct agg_3byte pass_agg_3byte(struct agg_3byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_3byte(i64 %{{.*}})
struct agg_4byte { char a[4]; };
struct agg_4byte pass_agg_4byte(struct agg_4byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_4byte(i64 %{{.*}})
struct agg_5byte { char a[5]; };
struct agg_5byte pass_agg_5byte(struct agg_5byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_5byte(i64 %{{.*}})
struct agg_6byte { char a[6]; };
struct agg_6byte pass_agg_6byte(struct agg_6byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_6byte(i64 %{{.*}})
struct agg_7byte { char a[7]; };
struct agg_7byte pass_agg_7byte(struct agg_7byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_7byte(i64 %{{.*}})
struct agg_8byte { char a[8]; };
struct agg_8byte pass_agg_8byte(struct agg_8byte arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_8byte(i64 %{{.*}})
struct agg_9byte { char a[9]; };
struct agg_9byte pass_agg_9byte(struct agg_9byte arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_agg_9byte([2 x i64] %{{.*}})
struct agg_16byte { char a[16]; };
struct agg_16byte pass_agg_16byte(struct agg_16byte arg) { return arg; }
// CHECK-LABEL: define inreg [2 x i64] @pass_agg_16byte([2 x i64] %{{.*}})
struct agg_24byte { char a[24]; };
struct agg_24byte pass_agg_24byte(struct agg_24byte arg) { return arg; }
// CHECK-LABEL: define inreg [3 x i64] @pass_agg_24byte([3 x i64] %{{.*}})
struct agg_25byte { char a[25]; };
struct agg_25byte pass_agg_25byte(struct agg_25byte arg) { return arg; }
// CHECK-LABEL: define void @pass_agg_25byte(ptr dead_on_unwind noalias writable sret{{.*}} align 1 %{{.*}}, [4 x i64] %{{.*}})
// Check that a float-like aggregate type is really passed as aggregate
struct agg_float { float a; };
struct agg_float pass_agg_float(struct agg_float arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_float(i64 %{{.*}})
// Verify that the following are *not* float-like aggregate types
struct agg_nofloat2 { float a; int b; };
struct agg_nofloat2 pass_agg_nofloat2(struct agg_nofloat2 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat2(i64 %{{.*}})
struct agg_nofloat3 { float a; int : 0; };
struct agg_nofloat3 pass_agg_nofloat3(struct agg_nofloat3 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_agg_nofloat3(i64 %{{.*}})
char * pass_pointer(char * arg) { return arg; }
// CHECK-LABEL: define ptr @pass_pointer(ptr %{{.*}})
typedef int vecint __attribute__ ((vector_size(16)));
vecint pass_vector_type(vecint arg) { return arg; }
// CHECKNOVEC-LABEL: define <4 x i32> @pass_vector_type(ptr align 8 dead_on_return %{{.*}})
// CHECKVEC-LABEL: define <4 x i32> @pass_vector_type(<4 x i32> %{{.*}})
// Union with just a single float element are treated as float inside a struct.
union u1 {
float m1, m2;
};
union u2 {
float m1;
union u1 m2;
};
union u3 {
float m1;
int m2;
};
struct complexlike_union1 {
float m1;
union u1 m2;
};
struct complexlike_union2 {
float m1;
union u2 m2;
};
struct complexlike_union3 {
union u1 m1;
union u2 m2;
};
struct normal_struct {
float m1;
union u3 m2;
};
struct complexlike_union1 pass_complexlike_union1(struct complexlike_union1 arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_union1 @pass_complexlike_union1({ float, float } %{{.*}})
struct complexlike_union2 pass_complexlike_union2(struct complexlike_union2 arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_union2 @pass_complexlike_union2({ float, float } %{{.*}})
struct complexlike_union3 pass_complexlike_union3(struct complexlike_union3 arg) { return arg; }
// CHECK-LABEL: define %struct.complexlike_union3 @pass_complexlike_union3({ float, float } %{{.*}})
union u1 pass_union1(union u1 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_union1(i64 %{{.*}})
union u2 pass_union2(union u2 arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_union2(i64 %{{.*}})
struct normal_struct pass_normal_struct(struct normal_struct arg) { return arg; }
// CHECK-LABEL: define inreg [1 x i64] @pass_normal_struct(i64 %{{.*}})
// ============================================================================
// Complex-like struct using nested typedefs with alignment attributes
// applied at different typedef layers.
// ============================================================================
typedef double raw_double;
// ============================================================================
// Alignment at first typedef layer
// ============================================================================
typedef align32_double nested_A;
struct S_A {
nested_A re;
double im;
};
struct S_A pass_S_A(struct S_A arg) { return arg; }
// CHECK-LABEL: define void @pass_S_A(
// CHECK-SAME: ptr {{.*}} sret(%struct.S_A) align 32
// CHECK-SAME: [4 x i64]
// CHECK: ret void
// ============================================================================
// Alignment applied at middle typedef layer
// ============================================================================
typedef double raw_B;
typedef raw_B middle_B __attribute__((aligned(32)));
typedef middle_B nested_B;
struct S_B {
double im;
nested_B re;
};
struct S_B pass_S_B(struct S_B arg) { return arg; }
// CHECK-LABEL: define void @pass_S_B(
// CHECK-SAME: ptr {{.*}} sret(%struct.S_B) align 32
// CHECK-SAME: [8 x i64]
// CHECK: ret void
// ============================================================================
// Alignment applied only at deepest typedef
// ============================================================================
typedef float raw_C0;
typedef raw_C0 raw_C1;
typedef raw_C1 raw_C2;
typedef raw_C2 nested_C_final __attribute__((aligned(32)));
struct S_C {
nested_C_final re;
float im;
};
struct S_C pass_S_C(struct S_C arg) { return arg; }
// CHECK-LABEL: define void @pass_S_C(
// CHECK-SAME: ptr {{.*}} sret(%struct.S_C) align 32
// CHECK-SAME: [4 x i64]
// CHECK: ret void
// ============================================================================
// Alignment on an intermediate typedef in a deeper chain
// ============================================================================
typedef float raw_D0;
typedef raw_D0 nested_D1;
typedef nested_D1 nested_D2 __attribute__((aligned(32)));
typedef nested_D2 nested_D3;
typedef nested_D3 nested_D;
struct S_D {
float im;
nested_D re;
};
struct S_D pass_S_D(struct S_D arg) { return arg; }
// CHECK-LABEL: define void @pass_S_D(
// CHECK-SAME: ptr {{.*}} sret(%struct.S_D) align 32
// CHECK-SAME: [8 x i64]
// CHECK: ret void
// ============================================================================
// NO alignment anywhere in the typedef chain
// Should NOT get 32-byte ABI alignment.
// ============================================================================
typedef double nested_E1;
typedef nested_E1 nested_E2;
typedef nested_E2 nested_E;
struct S_E {
nested_E re;
double im;
};
struct S_E pass_S_E(struct S_E arg) { return arg; }
// CHECK-LABEL: define %struct.S_E @pass_S_E(
// CHECK-SAME:{ double, double } %{{.*}})
// CHECK: ret %struct.S_E
// ============================================================================
// Over-aligned second field introduces internal padding, so the record
// cannot be treated as a complex-like `{ double, double }` and must be
// returned indirectly with 16-byte alignment.
// ============================================================================
struct Bad1 {
double re;
double im __attribute__((aligned(16)));;
};
struct Bad1 pass_Bad1(struct Bad1 arg) { return arg; }
// CHECK-LABEL: define void @pass_Bad1(ptr dead_on_unwind noalias writable sret({{.*}}) align 16 %agg.result, [4 x i64] %{{.*}})
// ============================================================================
// Stronger over-alignment on the second field further increases the required
// alignment and argument size, forcing indirect return with 32-byte alignment.
// ============================================================================
struct Bad2 {
double re;
double im __attribute__((aligned(32)));;
};
struct Bad2 pass_Bad2(struct Bad2 arg) { return arg; }
// CHECK-LABEL: define void @pass_Bad2(ptr dead_on_unwind noalias writable sret(%{{.*}}) align 32 %agg.result, [8 x i64] %{{.*}})
// ============================================================================
// Over-alignment on the first field prevents complex classification, but the
// record still fits in registers and is returned directly as a plain aggregate.
// ============================================================================
struct Bad3 {
double re __attribute__((aligned(16)));
double im;
};
struct Bad3 pass_Bad3(struct Bad3 arg) { return arg; }
// CHECK-LABEL: define %struct.Bad3 @pass_Bad3({ double, double } %{{.*}})
// ============================================================================
// Strong over-alignment on the first field raises the required alignment of the
// whole record, forcing indirect return and disqualifying complex-like passing.
// ============================================================================
struct Bad4 {
double re __attribute__((aligned(32)));
double im;
};
struct Bad4 pass_Bad4(struct Bad4 arg) { return arg; }
// CHECK-LABEL: define void @pass_Bad4(ptr dead_on_unwind noalias writable sret(%{{.*}}) align 32 %agg.result, [4 x i64] %{{.*}})
// ==================================================================
// Verify that transparent unions are passed like their first member.
// ==================================================================
union tu_char { char a; } __attribute__((transparent_union));
union tu_char pass_tu_char(union tu_char arg) { return arg; }
// CHECK-LABEL: define{{.*}} i8 @pass_tu_char(i8 signext %{{.*}})
union tu_short { short a; } __attribute__((transparent_union));
union tu_short pass_tu_short(union tu_short arg) { return arg; }
// CHECK-LABEL: define{{.*}} i16 @pass_tu_short(i16 signext %{{.*}})
union tu_int { int a; } __attribute__((transparent_union));
union tu_int pass_tu_int(union tu_int arg) { return arg; }
// CHECK-LABEL: define{{.*}} i32 @pass_tu_int(i32 signext %{{.*}})
union tu_long { long a; } __attribute__((transparent_union));
union tu_long pass_tu_long(union tu_long arg) { return arg; }
// CHECK-LABEL: define{{.*}} i64 @pass_tu_long(i64 signext %{{.*}})
union tu_ptr { void *a; } __attribute__((transparent_union));
union tu_ptr pass_tu_ptr(union tu_ptr arg) { return arg; }
// CHECK-LABEL: define{{.*}} ptr @pass_tu_ptr(ptr %{{.*}})
// =================================================================
// Accessing variable argument lists
// =================================================================
// z/OS has two different implementations for variable argument handling.
// Functions ending with _e test the extended variant of vararg functions
// (__builtin_va_start, __builtin_va_arg, __builtin_va_end). The type of
// va_list is __builtin_va_list.
// Functions ending with _s test the standard variant of vararg functions
// (__builtin_zos_va_start, __builtin_va_arg, __builtin_zos_va_end). The type of
// va_list is __builtin_va_list.
int dofmt_e(const char *fmt, ...) {
__builtin_va_list va;
__builtin_va_start(va, fmt);
int v = __builtin_va_arg(va, int);
__builtin_va_end(va);
return v;
}
// CHECK-LABEL: define signext i32 @dofmt_e(ptr %{{.*}}, ...)
// CHECK: [[FMT_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: [[VA:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: [[V:%[._a-z0-9]+]] = alloca i32, align 4
// CHECK: store ptr %{{.*}}, ptr [[FMT_ADDR]], align 8
// CHECK: call void @llvm.va_start.p0(ptr [[VA]])
// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[VA]], align 8
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
// CHECK: store ptr [[ARGP_NEXT]], ptr [[VA]], align 8
// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 4
// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
// CHECK: store i32 [[VAL]], ptr [[V]], align 4
// CHECK: call void @llvm.va_end.p0(ptr [[VA]])
// CHECK: [[VAL2:%[._a-z0-9]+]] = load i32, ptr [[V]], align 4
// CHECK: ret i32 [[VAL2]]
int dofmt_s(const char *fmt, ...) {
__builtin_zos_va_list va;
__builtin_zos_va_start(va, fmt);
int v = __builtin_va_arg(va, int);
__builtin_zos_va_end(va);
return v;
}
// CHECK-LABEL: define signext i32 @dofmt_s(ptr %{{.*}}, ...)
// CHECK: [[FMT_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: [[VA:%[._a-z0-9]+]] = alloca [2 x ptr], align 8
// CHECK: [[V:%[._a-z0-9]+]] = alloca i32, align 4
// CHECK: store ptr %{{.*}}, ptr [[FMT_ADDR]], align 8
// CHECK: [[DECAY1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
// CHECK: [[VALIST_CURR1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY1]], i64 0, i64 0
// CHECK: store ptr null, ptr [[VALIST_CURR1]], align 8
// CHECK: [[VALIST_NEXT1:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY1]], i64 0, i64 1
// CHECK: call void @llvm.va_start.p0(ptr [[VALIST_NEXT1]])
// CHECK: [[DECAY2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
// CHECK: [[VALIST_CURR2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY2]], i64 0, i64 0
// CHECK: [[VALIST_NEXT2:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY2]], i64 0, i64 1
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT2]], align 8
// CHECK: %0 = getelementptr inbounds i8, ptr [[ARGP_NEXT]], i32 7
// CHECK: [[ARGP_NEXT_ALIGNED:%[._a-z0-9]+]] = call ptr @llvm.ptrmask.p0.i64(ptr %0, i64 -8)
// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR2]], align 8
// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT2]], align 8
// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
// CHECK: store i32 [[VAL]], ptr [[V]], align 4
// CHECK: [[DECAY3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VA]], i64 0, i64 0
// CHECK: [[VALIST_CURR3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY3]], i64 0, i64 0
// CHECK: store ptr null, ptr [[VALIST_CURR3]], align 8
// CHECK: [[VALIST_NEXT3:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[DECAY3]], i64 0, i64 1
// CHECK: call void @llvm.va_end.p0(ptr [[VALIST_NEXT3]])
// CHECK: [[VAL2:%[._a-z0-9]+]] = load i32, ptr [[V]], align 4
// CHECK: ret i32 [[VAL2]]
int va_int_e(__builtin_va_list l) { return __builtin_va_arg(l, int); }
// CHECK-LABEL: define signext i32 @va_int_e(ptr %{{.*}})
// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
// CHECK: store ptr [[ARGP_NEXT]], ptr [[L_ADDR]], align 8
// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 4
// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
// CHECK: ret i32 [[VAL]]
int va_int_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, int); }
// CHECK-LABEL: define signext i32 @va_int_s(ptr %{{.*}})
// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
// CHECK: [[ARGP_NEXT_ALIGNED]] = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
// CHECK: [[V_ADDR:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 4
// CHECK: [[VAL:%[._a-z0-9]+]] = load i32, ptr [[V_ADDR]], align 4
// CHECK: ret i32 [[VAL]]
long va_long_e(__builtin_va_list l) { return __builtin_va_arg(l, long); }
// CHECK-LABEL: define signext i64 @va_long_e(ptr %{{.*}})
// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
// CHECK: [[ARGP_CURR:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_CURR]], i64 8
// CHECK: store ptr [[ARGP_NEXT]], ptr [[L_ADDR]], align 8
// CHECK: [[VAL:%[._a-z0-9]+]] = load i64, ptr [[ARGP_CURR]], align 8
// CHECK: ret i64 [[VAL]]
long va_long_s(__builtin_zos_va_list l) { return __builtin_va_arg(l, long); }
// CHECK-LABEL: define signext i64 @va_long_s(ptr %{{.*}})
// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
// CHECK: %arg.next.aligned = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 8
// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
// CHECK: [[VAL:%[._a-z0-9]+]] = load i64, ptr [[ARGP_NEXT_ALIGNED]], align 8
// CHECK: ret i64 [[VAL]]
struct agg_threedouble {
double a, b, c;
};
struct agg_threedouble va_3double_s(__builtin_zos_va_list l) {
return __builtin_va_arg(l, struct agg_threedouble);
}
// CHECK-LABEL: define inreg [3 x i64] @va_3double_s(ptr %{{.*}})
// CHECK: [[RETVAL:%[._a-z0-9]+]] = alloca %struct.agg_threedouble, align 8
// CHECK: [[L_ADDR:%[._a-z0-9]+]] = alloca ptr, align 8
// CHECK: store ptr %{{.*}}, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST:%[._a-z0-9]+]] = load ptr, ptr [[L_ADDR]], align 8
// CHECK: [[VALIST_CURR:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 0
// CHECK: [[VALIST_NEXT:%[._a-z0-9]+]] = getelementptr inbounds [2 x ptr], ptr [[VALIST]], i64 0, i64 1
// CHECK: [[ARGP_NEXT:%[._a-z0-9]+]] = load ptr, ptr [[VALIST_NEXT]], align 8
// CHECK: %1 = getelementptr inbounds i8, ptr %arg.next, i32 7
// CHECK: [[ARGP_NEXT_ALIGNED]] = call ptr @llvm.ptrmask.p0.i64(ptr %1, i64 -8)
// CHECK: store ptr [[ARGP_NEXT_ALIGNED]], ptr [[VALIST_CURR]], align 8
// CHECK: [[ARGP_NEXT_NEXT:%[._a-z0-9]+]] = getelementptr inbounds i8, ptr [[ARGP_NEXT_ALIGNED]], i64 24
// CHECK: store ptr [[ARGP_NEXT_NEXT]], ptr [[VALIST_NEXT]], align 8
// CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[RETVAL]], ptr align 8 [[ARGP_NEXT_ALIGNED]], i64 24, i1 false)
// CHECK: [[RETVAL2:%[._a-z0-9]+]] = load [3 x i64], ptr [[RETVAL]], align 8
// CHECK: ret [3 x i64] [[RETVAL2]]