| // 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 nofreeobj align 8 dead_on_return dereferenceable(16) %{{.*}}) | |
| // 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]] |