Index: clang/test/CodeGen/RISCV/riscv32-abi.c =================================================================== --- /dev/null +++ clang/test/CodeGen/RISCV/riscv32-abi.c @@ -0,0 +1,1524 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 +// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32-ILP32F,ILP32 %s +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32-ILP32F,ILP32F %s +// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-feature +d -target-abi ilp32d -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=ILP32-ILP32F-ILP32D,ILP32F-ILP32D,ILP32D %s + +#include +#include + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_void +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0:[0-9]+]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_void(void) {} + +// Scalar arguments and return values smaller than the word size are extended +// according to the sign of their type, up to 32 bits + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local zeroext i1 @f_scalar_0 +// ILP32-ILP32F-ILP32D-SAME: (i1 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +_Bool f_scalar_0(_Bool x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local signext i8 @f_scalar_1 +// ILP32-ILP32F-ILP32D-SAME: (i8 noundef signext [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +int8_t f_scalar_1(int8_t x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local zeroext i8 @f_scalar_2 +// ILP32-ILP32F-ILP32D-SAME: (i8 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +uint8_t f_scalar_2(uint8_t x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_scalar_3 +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +int32_t f_scalar_3(int32_t x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i64 @f_scalar_4 +// ILP32-ILP32F-ILP32D-SAME: (i64 noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +int64_t f_scalar_4(int64_t x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local float @f_fp_scalar_1 +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +float f_fp_scalar_1(float x) { return x; } + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local double @f_fp_scalar_2 +// ILP32-ILP32F-ILP32D-SAME: (double noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +double f_fp_scalar_2(double x) { return x; } + +// Scalars larger than 2*xlen are passed/returned indirect. However, the +// RISC-V LLVM backend can handle this fine, so the function doesn't need to +// be modified. + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local fp128 @f_fp_scalar_3 +// ILP32-ILP32F-ILP32D-SAME: (fp128 noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +long double f_fp_scalar_3(long double x) { return x; } + +// Empty structs or unions are ignored. + +struct empty_s {}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_empty_struct +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct empty_s f_agg_empty_struct(struct empty_s x) { + return x; +} + +union empty_u {}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_empty_union +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +union empty_u f_agg_empty_union(union empty_u x) { + return x; +} + +// Aggregates <= 2*xlen may be passed in registers, so will be coerced to +// integer arguments. The rules for return are the same. + +struct tiny { + uint8_t a, b, c, d; +}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_tiny +// ILP32-ILP32F-ILP32D-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_agg_tiny(struct tiny x) { + x.a += x.b; + x.c += x.d; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_agg_tiny_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct tiny f_agg_tiny_ret(void) { + return (struct tiny){1, 2, 3, 4}; +} + +typedef uint8_t v4i8 __attribute__((vector_size(4))); +typedef int32_t v1i32 __attribute__((vector_size(4))); + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_tiny_v4i8 +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_vec_tiny_v4i8(v4i8 x) { + x[0] = x[1]; + x[2] = x[3]; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_vec_tiny_v4i8_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +v4i8 f_vec_tiny_v4i8_ret(void) { + return (v4i8){1, 2, 3, 4}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_tiny_v1i32 +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_vec_tiny_v1i32(v1i32 x) { + x[0] = 114; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_vec_tiny_v1i32_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +v1i32 f_vec_tiny_v1i32_ret(void) { + return (v1i32){1}; +} + +struct small { + int32_t a, *b; +}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_small +// ILP32-ILP32F-ILP32D-SAME: ([2 x i32] [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_agg_small(struct small x) { + x.a += *x.b; + x.b = &x.a; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local [2 x i32] @f_agg_small_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct small f_agg_small_ret(void) { + return (struct small){1, 0}; +} + +typedef uint8_t v8i8 __attribute__((vector_size(8))); +typedef int64_t v1i64 __attribute__((vector_size(8))); + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_small_v8i8 +// ILP32-ILP32F-ILP32D-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_vec_small_v8i8(v8i8 x) { + x[0] = x[7]; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i64 @f_vec_small_v8i8_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +v8i8 f_vec_small_v8i8_ret(void) { + return (v8i8){1, 2, 3, 4, 5, 6, 7, 8}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_small_v1i64 +// ILP32-ILP32F-ILP32D-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_vec_small_v1i64(v1i64 x) { + x[0] = 114; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i64 @f_vec_small_v1i64_ret +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +v1i64 f_vec_small_v1i64_ret(void) { + return (v1i64){1}; +} + +// Aggregates of 2*xlen size and 2*xlen alignment should be coerced to a +// single 2*xlen-sized argument, to ensure that alignment can be maintained if +// passed on the stack. + +struct small_aligned { + int64_t a; +}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_small_aligned +// ILP32-ILP32F-ILP32D-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_agg_small_aligned(struct small_aligned x) { + x.a += x.a; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i64 @f_agg_small_aligned_ret +// ILP32-ILP32F-ILP32D-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { + return (struct small_aligned){10}; +} + +// Aggregates greater > 2*xlen will be passed and returned indirectly +struct large { + int32_t a, b, c, d; +}; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_large +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[X:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_agg_large(struct large x) { + x.a = x.b + x.c + x.d; +} + +// The address where the struct should be written to will be the first +// argument +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_large_ret +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[I:%.*]], i8 noundef signext [[J:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct large f_agg_large_ret(int32_t i, int8_t j) { + return (struct large){1, 2, 3, 4}; +} + +typedef unsigned char v16i8 __attribute__((vector_size(16))); + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_large_v16i8 +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_vec_large_v16i8(v16i8 x) { + x[0] = x[7]; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_vec_large_v16i8_ret +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret(<16 x i8>) align 16 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +v16i8 f_vec_large_v16i8_ret(void) { + return (v16i8){1, 2, 3, 4, 5, 6, 7, 8}; +} + +// Scalars passed on the stack should have signext/zeroext attributes, just as +// if they were passed in registers. + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_scalar_stack_1 +// ILP32-ILP32F-ILP32D-SAME: (i32 [[A_COERCE:%.*]], [2 x i32] [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, + struct large d, uint8_t e, int8_t f, uint8_t g, int8_t h) { + return g + h; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_scalar_stack_2 +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct large f_scalar_stack_2(int32_t a, int64_t b, int64_t c, long double d, + uint8_t e, int8_t f, uint8_t g) { + return (struct large){a, e, f, g}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local fp128 @f_scalar_stack_3 +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +long double f_scalar_stack_3(int32_t a, int64_t b, int64_t c, long double d, + uint8_t e, int8_t f, uint8_t g) { + return d; +} + +// Aggregates and >=XLen scalars passed on the stack should be lowered just as +// they would be if passed via registers. + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_scalar_stack_4 +// ILP32-ILP32F-ILP32D-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], float noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_scalar_stack_4(double a, int64_t b, double c, int64_t d, int e, + int64_t f, float g, double h, long double i) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_scalar_stack_5 +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], float noundef [[C:%.*]], double noundef [[D:%.*]], fp128 noundef [[E:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +int f_scalar_stack_5(int32_t a, int64_t b, float c, double d, long double e, + uint8_t f, int8_t g, uint8_t h) { + return g + h; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_scalar_stack_6 +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], float noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct large f_scalar_stack_6(float a, int64_t b, double c, long double d, + uint8_t e, int8_t f, uint8_t g) { + return (struct large){a, e, f, g}; +} + +// Aggregates and >=XLen scalars passed on the stack should be lowered just as +// they would be if passed via registers. + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_agg_stack +// ILP32-ILP32F-ILP32D-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, + struct small f, struct small_aligned g, struct large h) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_fpr_tracking +// ILP32-ILP32F-ILP32D-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], i8 noundef signext [[E:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_fpr_tracking(double a, double b, double c, double d, int8_t e) {} + +// Lowering for doubles is unnmodified, as 64 > FLEN. + +struct double_s { double d; }; + +struct double_double_s { double d; double e; }; + +struct double_int8_s { double d; int64_t i; }; + +struct int_double_s { int a; double b; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_int_double_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_int_double_s_arg +// ILP32D-SAME: (i32 [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_int_double_s_arg(struct int_double_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_int_double_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { i32, double } @f_ret_int_double_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct int_double_s f_ret_int_double_s(void) { + return (struct int_double_s){1, 2.0}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_fpr_tracking2 +// ILP32-ILP32F-ILP32D-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_fpr_tracking2(double a, double b, double c, double d, double e, double f, + double g, double h, uint8_t i) {} + +// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will +// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are +// available the widths are <= XLEN and FLEN, and should be expanded to +// separate arguments in IR. They are passed by the same rules for returns, +// but will be lowered to simple two-element structs if necessary (as LLVM IR +// functions cannot return multiple values). + +// A struct containing just one floating-point real is passed as though it +// were a standalone floating-point real. + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_s_arg +// ILP32-ILP32F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_s_arg(struct double_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local i64 @f_ret_double_s +// ILP32-ILP32F-SAME: () #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local double @f_ret_double_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_s f_ret_double_s(void) { + return (struct double_s){1.0}; +} + +// A struct containing a double and any number of zero-width bitfields is +// passed as though it were a standalone floating-point real. + +struct zbf_double_s { int : 0; double f; }; +struct zbf_double_zbf_s { int : 0; double f; int : 0; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_zbf_double_s_arg +// ILP32-ILP32F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_zbf_double_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_zbf_double_s_arg(struct zbf_double_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local i64 @f_ret_zbf_double_s +// ILP32-ILP32F-SAME: () #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local double @f_ret_zbf_double_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct zbf_double_s f_ret_zbf_double_s(void) { + return (struct zbf_double_s){1.0}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_zbf_double_zbf_s_arg +// ILP32-ILP32F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_zbf_double_zbf_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local i64 @f_ret_zbf_double_zbf_s +// ILP32-ILP32F-SAME: () #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local double @f_ret_zbf_double_zbf_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct zbf_double_zbf_s f_ret_zbf_double_zbf_s(void) { + return (struct zbf_double_zbf_s){1.0}; +} + +// Check that structs containing two floating point values (FLEN <= width) are +// expanded provided sufficient FPRs are available. + +struct double_float_s { double f; float g; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_double_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_double_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_double_s_arg(struct double_double_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_double_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_double_double_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_double_s f_ret_double_double_s(void) { + return (struct double_double_s){1.0, 2.0}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_float_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_float_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_float_s_arg(struct double_float_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_float_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_FLOAT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, float } @f_ret_double_float_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_float_s f_ret_double_float_s(void) { + return (struct double_float_s){1.0, 2.0}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_double_s_arg_insufficient_fprs +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d, + double e, double f, double g, struct double_double_s h) {} + +// Check that structs containing int+double values are expanded, provided +// sufficient FPRs and GPRs are available. The integer components are neither +// sign or zero-extended. + +struct double_uint8_s { double d; uint8_t i; }; +struct double_int32_s { double d; int32_t i; }; +struct double_int64_s { double d; int64_t i; }; +struct double_int64bf_s { double d; int64_t i : 32; }; +struct double_int8_zbf_s { double d; int8_t i; int : 0; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_int8_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_double_int8_s_arg(struct double_int8_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_double_int8_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT8_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct double_int8_s f_ret_double_int8_s(void) { + return (struct double_int8_s){1.0, 2}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_uint8_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_uint8_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_uint8_s_arg(struct double_uint8_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_uint8_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_UINT8_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, i8 } @f_ret_double_uint8_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_uint8_s f_ret_double_uint8_s(void) { + return (struct double_uint8_s){1.0, 2}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_int32_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_int32_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_int32_s_arg(struct double_int32_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_int32_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT32_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_int32_s f_ret_double_int32_s(void) { + return (struct double_int32_s){1.0, 2}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_int64_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_double_int64_s_arg(struct double_int64_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_double_int64_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct double_int64_s f_ret_double_int64_s(void) { + return (struct double_int64_s){1.0, 2}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_int64bf_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_int64bf_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_int64bf_s_arg(struct double_int64bf_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_int64bf_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT64BF_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, i32 } @f_ret_double_int64bf_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_int64bf_s f_ret_double_int64bf_s(void) { + return (struct double_int64bf_s){1.0, 2}; +} + +// The zero-width bitfield means the struct can't be passed according to the +// floating point calling convention. + +// ILP32-ILP32F-LABEL: define dso_local void @f_double_int8_zbf_s +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_double_int8_zbf_s +// ILP32D-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_double_int8_zbf_s(struct double_int8_zbf_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_int8_zbf_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT8_ZBF_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, i8 } @f_ret_double_int8_zbf_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct double_int8_zbf_s f_ret_double_int8_zbf_s(void) { + return (struct double_int8_zbf_s){1.0, 2}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, + int f, int g, int h, struct double_int8_s i) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_struct_double_int8_insufficient_fprs +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d, + double e, double f, double g, double h, struct double_int8_s i) {} + +// Complex floating-point values or structs containing a single complex +// floating-point value should be passed as if it were an fp+fp struct. + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublecomplex +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublecomplex +// ILP32D-SAME: (double noundef [[A_COERCE0:%.*]], double noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublecomplex(double __complex__ a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublecomplex +// ILP32-ILP32F-SAME: (ptr noalias sret({ double, double }) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublecomplex +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +double __complex__ f_ret_doublecomplex(void) { + return 1.0; +} + +struct doublecomplex_s { double __complex__ c; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublecomplex_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublecomplex_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublecomplex_s_arg(struct doublecomplex_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublecomplex_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLECOMPLEX_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublecomplex_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublecomplex_s f_ret_doublecomplex_s(void) { + return (struct doublecomplex_s){1.0}; +} + +// Test single or two-element structs that need flattening. e.g. those +// containing nested structs, doubles in small arrays, zero-length structs etc. + +struct doublearr1_s { double a[1]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr1_s_arg +// ILP32-ILP32F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr1_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr1_s_arg(struct doublearr1_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local i64 @f_ret_doublearr1_s +// ILP32-ILP32F-SAME: () #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local double @f_ret_doublearr1_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr1_s f_ret_doublearr1_s(void) { + return (struct doublearr1_s){{1.0}}; +} + +struct doublearr2_s { double a[2]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr2_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr2_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr2_s_arg(struct doublearr2_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublearr2_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLEARR2_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublearr2_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr2_s f_ret_doublearr2_s(void) { + return (struct doublearr2_s){{1.0, 2.0}}; +} + +struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublearr2_tricky1_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLEARR2_TRICKY1_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky1_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s(void) { + return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublearr2_tricky2_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLEARR2_TRICKY2_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky2_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s(void) { + return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublearr2_tricky3_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLEARR2_TRICKY3_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky3_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s(void) { + return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; }; + +// ILP32-ILP32F-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg +// ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg +// ILP32D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublearr2_tricky4_s +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLEARR2_TRICKY4_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky4_s +// ILP32D-SAME: () #[[ATTR0]] { +// ILP32D: entry: +// +struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s(void) { + return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; +} + +// Test structs that should be passed according to the normal integer calling +// convention. + +struct int_double_int_s { int a; double b; int c; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int_double_int_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_int_double_int_s_arg(struct int_double_int_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int_double_int_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_INT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct int_double_int_s f_ret_int_double_int_s(void) { + return (struct int_double_int_s){1, 2.0, 3}; +} + +struct int64_double_s { int64_t a; double b; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int64_double_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_int64_double_s_arg(struct int64_double_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int64_double_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT64_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct int64_double_s f_ret_int64_double_s(void) { + return (struct int64_double_s){1, 2.0}; +} + +struct char_char_double_s { char a; char b; double c; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_char_char_double_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_char_char_double_s_arg(struct char_char_double_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_char_char_double_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_CHAR_CHAR_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct char_char_double_s f_ret_char_char_double_s(void) { + return (struct char_char_double_s){1, 2, 3.0}; +} + +// Unions are always passed according to the integer calling convention, even +// if they can only contain a double. + +union double_u { double a; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_u_arg +// ILP32-ILP32F-ILP32D-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_double_u_arg(union double_u a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i64 @f_ret_double_u +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +union double_u f_ret_double_u(void) { + return (union double_u){1.0}; +} + +// Test that we don't incorrectly think double+int/double+double structs will +// be returned indirectly and thus have an off-by-one error for the number of +// GPRs available (this is an edge case when structs > 2*XLEN are still +// returned in registers). This includes complex doubles, which are treated as +// double+double structs by the ABI. + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_int32_s_double_int32_s_just_sufficient_gprs +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT32_S:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s_double_int32_s_just_sufficient_gprs +// ILP32D-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +struct double_int32_s f_ret_double_int32_s_double_int32_s_just_sufficient_gprs( + int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { + return (struct double_int32_s){1.0, 2}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_double_s_double_int32_s_just_sufficient_gprs +// ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_double_double_s_double_int32_s_just_sufficient_gprs +// ILP32D-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +struct double_double_s f_ret_double_double_s_double_int32_s_just_sufficient_gprs( + int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { + return (struct double_double_s){1.0, 2.0}; +} + +// ILP32-ILP32F-LABEL: define dso_local void @f_ret_doublecomplex_double_int32_s_just_sufficient_gprs +// ILP32-ILP32F-SAME: (ptr noalias sret({ double, double }) align 8 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F: entry: +// +// ILP32D-LABEL: define dso_local { double, double } @f_ret_doublecomplex_double_int32_s_just_sufficient_gprs +// ILP32D-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32D: entry: +// +double __complex__ f_ret_doublecomplex_double_int32_s_just_sufficient_gprs( + int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { + return 1.0; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_fpr_tracking_3 +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_fpr_tracking_3(float a, float b, float c, float d, float e, float f, + float g, float h, uint8_t i) {} + +// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will +// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are +// available the widths are <= XLEN and FLEN, and should be expanded to +// separate arguments in IR. They are passed by the same rules for returns, +// but will be lowered to simple two-element structs if necessary (as LLVM IR +// functions cannot return multiple values). + +// A struct containing just one floating-point real is passed as though it +// were a standalone floating-point real. + +struct float_s { float f; }; + +// ILP32-LABEL: define dso_local void @f_float_s_arg +// ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_s_arg(struct float_s a) {} + +// ILP32-LABEL: define dso_local i32 @f_ret_float_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local float @f_ret_float_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_s f_ret_float_s(void) { + return (struct float_s){1.0}; +} + +// A struct containing a float and any number of zero-width bitfields is +// passed as though it were a standalone floating-point real. + +struct zbf_float_s { int : 0; float f; }; +struct zbf_float_zbf_s { int : 0; float f; int : 0; }; + +// ILP32-LABEL: define dso_local void @f_zbf_float_s_arg +// ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_zbf_float_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_zbf_float_s_arg(struct zbf_float_s a) {} + +// ILP32-LABEL: define dso_local i32 @f_ret_zbf_float_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local float @f_ret_zbf_float_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct zbf_float_s f_ret_zbf_float_s(void) { + return (struct zbf_float_s){1.0}; +} + +// ILP32-LABEL: define dso_local void @f_zbf_float_zbf_s_arg +// ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_zbf_float_zbf_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {} + +// ILP32-LABEL: define dso_local i32 @f_ret_zbf_float_zbf_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local float @f_ret_zbf_float_zbf_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct zbf_float_zbf_s f_ret_zbf_float_zbf_s(void) { + return (struct zbf_float_zbf_s){1.0}; +} + +// Check that structs containing two float values (FLEN <= width) are expanded +// provided sufficient FPRs are available. + +struct float_float_s { float f; float g; }; + +// ILP32-LABEL: define dso_local void @f_float_float_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_float_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_float_s_arg(struct float_float_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_float_float_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_float_float_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_float_s f_ret_float_float_s(void) { + return (struct float_float_s){1.0, 2.0}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float_float_s_arg_insufficient_fprs +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], [2 x i32] [[H_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, + float e, float f, float g, struct float_float_s h) {} + +// Check that structs containing int+float values are expanded, provided +// sufficient FPRs and GPRs are available. The integer components are neither +// sign or zero-extended. + +struct float_int8_s { float f; int8_t i; }; +struct float_uint8_s { float f; uint8_t i; }; +struct float_int32_s { float f; int32_t i; }; +struct float_int64_s { float f; int64_t i; }; +struct float_int64bf_s { float f; int64_t i : 32; }; +struct float_int8_zbf_s { float f; int8_t i; int : 0; }; + +// ILP32-LABEL: define dso_local void @f_float_int8_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_int8_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_int8_s_arg(struct float_int8_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_float_int8_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, i8 } @f_ret_float_int8_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_int8_s f_ret_float_int8_s(void) { + return (struct float_int8_s){1.0, 2}; +} + +// ILP32-LABEL: define dso_local void @f_float_uint8_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_uint8_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_uint8_s_arg(struct float_uint8_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_float_uint8_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, i8 } @f_ret_float_uint8_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_uint8_s f_ret_float_uint8_s(void) { + return (struct float_uint8_s){1.0, 2}; +} + +// ILP32-LABEL: define dso_local void @f_float_int32_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_int32_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_int32_s_arg(struct float_int32_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_float_int32_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, i32 } @f_ret_float_int32_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_int32_s f_ret_float_int32_s(void) { + return (struct float_int32_s){1.0, 2}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float_int64_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_float_int64_s_arg(struct float_int64_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_float_int64_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_FLOAT_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct float_int64_s f_ret_float_int64_s(void) { + return (struct float_int64_s){1.0, 2}; +} + +// ILP32-LABEL: define dso_local void @f_float_int64bf_s_arg +// ILP32-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_int64bf_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_int64bf_s_arg(struct float_int64bf_s a) {} + +// ILP32-LABEL: define dso_local i64 @f_ret_float_int64bf_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, i32 } @f_ret_float_int64bf_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_int64bf_s f_ret_float_int64bf_s(void) { + return (struct float_int64bf_s){1.0, 2}; +} + +// The zero-width bitfield means the struct can't be passed according to the +// floating point calling convention. + +// ILP32-LABEL: define dso_local void @f_float_int8_zbf_s +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_float_int8_zbf_s +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_float_int8_zbf_s(struct float_int8_zbf_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_float_int8_zbf_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, i8 } @f_ret_float_int8_zbf_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct float_int8_zbf_s f_ret_float_int8_zbf_s(void) { + return (struct float_int8_zbf_s){1.0, 2}; +} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs +// ILP32-ILP32F-ILP32D-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, + int f, int g, int h, struct float_int8_s i) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_struct_float_int8_insufficient_fprs +// ILP32-ILP32F-ILP32D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d, + float e, float f, float g, float h, struct float_int8_s i) {} + +// Complex floating-point values or structs containing a single complex +// floating-point value should be passed as if it were an fp+fp struct. + +// ILP32-LABEL: define dso_local void @f_floatcomplex +// ILP32-SAME: ([2 x i32] noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatcomplex +// ILP32F-ILP32D-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatcomplex(float __complex__ a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatcomplex +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatcomplex +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +float __complex__ f_ret_floatcomplex(void) { + return 1.0; +} + +struct floatcomplex_s { float __complex__ c; }; + +// ILP32-LABEL: define dso_local void @f_floatcomplex_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatcomplex_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatcomplex_s_arg(struct floatcomplex_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatcomplex_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatcomplex_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatcomplex_s f_ret_floatcomplex_s(void) { + return (struct floatcomplex_s){1.0}; +} + +// Test single or two-element structs that need flattening. e.g. those +// containing nested structs, floats in small arrays, zero-length structs etc. + +struct floatarr1_s { float a[1]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr1_s_arg +// ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr1_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr1_s_arg(struct floatarr1_s a) {} + +// ILP32-LABEL: define dso_local i32 @f_ret_floatarr1_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local float @f_ret_floatarr1_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr1_s f_ret_floatarr1_s(void) { + return (struct floatarr1_s){{1.0}}; +} + +struct floatarr2_s { float a[2]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr2_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr2_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr2_s_arg(struct floatarr2_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatarr2_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatarr2_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr2_s f_ret_floatarr2_s(void) { + return (struct floatarr2_s){{1.0, 2.0}}; +} + +struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatarr2_tricky1_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky1_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s(void) { + return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatarr2_tricky2_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky2_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s(void) { + return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatarr2_tricky3_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky3_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s(void) { + return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; }; + +// ILP32-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg +// ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg +// ILP32F-ILP32D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {} + +// ILP32-LABEL: define dso_local [2 x i32] @f_ret_floatarr2_tricky4_s +// ILP32-SAME: () #[[ATTR0]] { +// ILP32: entry: +// +// ILP32F-ILP32D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky4_s +// ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32F-ILP32D: entry: +// +struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s(void) { + return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; +} + +// Test structs that should be passed according to the normal integer calling +// convention. + +struct int_float_int_s { int a; float b; int c; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int_float_int_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_int_float_int_s_arg(struct int_float_int_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int_float_int_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT_FLOAT_INT_S:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct int_float_int_s f_ret_int_float_int_s(void) { + return (struct int_float_int_s){1, 2.0, 3}; +} + +struct int64_float_s { int64_t a; float b; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int64_float_s_arg +// ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_int64_float_s_arg(struct int64_float_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int64_float_s +// ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT64_FLOAT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct int64_float_s f_ret_int64_float_s(void) { + return (struct int64_float_s){1, 2.0}; +} + +struct char_char_float_s { char a; char b; float c; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_char_char_float_s_arg +// ILP32-ILP32F-ILP32D-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_char_char_float_s_arg(struct char_char_float_s a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local [2 x i32] @f_ret_char_char_float_s +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +struct char_char_float_s f_ret_char_char_float_s(void) { + return (struct char_char_float_s){1, 2, 3.0}; +} + +// Unions are always passed according to the integer calling convention, even +// if they can only contain a float. + +union float_u { float a; }; + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float_u_arg +// ILP32-ILP32F-ILP32D-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +void f_float_u_arg(union float_u a) {} + +// ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_ret_float_u +// ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { +// ILP32-ILP32F-ILP32D: entry: +// +union float_u f_ret_float_u(void) { + return (union float_u){1.0}; +} +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// ILP32F: {{.*}} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-abi.c +++ /dev/null @@ -1,62 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s - -// This file contains test cases that will have different output for ilp32 vs -// the other 32-bit ABIs. - -#include -#include - -struct tiny { - uint8_t a, b, c, d; -}; - -struct small { - int32_t a, *b; -}; - -struct small_aligned { - int64_t a; -}; - -struct large { - int32_t a, b, c, d; -}; - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers. - -// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 -// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], float noundef [[C:%.*]], double noundef [[D:%.*]], fp128 noundef [[E:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -int f_scalar_stack_1(int32_t a, int64_t b, float c, double d, long double e, - uint8_t f, int8_t g, uint8_t h) { - return g + h; -} - -// CHECK-LABEL: define dso_local void @f_scalar_stack_2 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], float noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_2(float a, int64_t b, double c, long double d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} - -// Aggregates and >=XLen scalars passed on the stack should be lowered just as -// they would be if passed via registers. - -// CHECK-LABEL: define dso_local void @f_scalar_stack_3 -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], float noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_scalar_stack_3(double a, int64_t b, double c, int64_t d, int e, - int64_t f, float g, double h, long double i) {} - -// CHECK-LABEL: define dso_local void @f_agg_stack -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, - struct small f, struct small_aligned g, struct large h) {} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-abi.c +++ /dev/null @@ -1,64 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -// This file contains test cases that will have the same output for the ilp32 -// and ilp32f ABIs. - -#include -#include - -struct tiny { - uint8_t a, b, c, d; -}; - -struct small { - int32_t a, *b; -}; - -struct small_aligned { - int64_t a; -}; - -struct large { - int32_t a, b, c, d; -}; - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers. - -// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 -// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i32 noundef [[C:%.*]], double noundef [[D:%.*]], fp128 noundef [[E:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -int f_scalar_stack_1(int32_t a, int64_t b, int32_t c, double d, long double e, - uint8_t f, int8_t g, uint8_t h) { - return g + h; -} - -// CHECK-LABEL: define dso_local void @f_scalar_stack_2 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_2(int32_t a, int64_t b, double c, long double d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} - -// Aggregates and >=XLen scalars passed on the stack should be lowered just as -// they would be if passed via registers. - -// CHECK-LABEL: define dso_local void @f_scalar_stack_3 -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], i32 noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_scalar_stack_3(double a, int64_t b, double c, int64_t d, int e, - int64_t f, int32_t g, double h, long double i) {} - -// CHECK-LABEL: define dso_local void @f_agg_stack -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, - struct small f, struct small_aligned g, struct large h) {} Index: clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32-ilp32f-ilp32d-abi.c +++ /dev/null @@ -1,337 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple riscv32 -emit-llvm -fforce-enable-int128 %s -o - \ -// RUN: | FileCheck %s -check-prefixes=CHECK,CHECK-FORCEINT128 -// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ -// RUN: | FileCheck %s -// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -// This file contains test cases that will have the same output for the ilp32, -// ilp32f, and ilp32d ABIs. - -#include -#include - -// CHECK-LABEL: define dso_local void @f_void -// CHECK-SAME: () #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_void(void) {} - -// Scalar arguments and return values smaller than the word size are extended -// according to the sign of their type, up to 32 bits - -// CHECK-LABEL: define dso_local zeroext i1 @f_scalar_0 -// CHECK-SAME: (i1 noundef zeroext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -_Bool f_scalar_0(_Bool x) { return x; } - -// CHECK-LABEL: define dso_local signext i8 @f_scalar_1 -// CHECK-SAME: (i8 noundef signext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int8_t f_scalar_1(int8_t x) { return x; } - -// CHECK-LABEL: define dso_local zeroext i8 @f_scalar_2 -// CHECK-SAME: (i8 noundef zeroext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -uint8_t f_scalar_2(uint8_t x) { return x; } - -// CHECK-LABEL: define dso_local i32 @f_scalar_3 -// CHECK-SAME: (i32 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int32_t f_scalar_3(int32_t x) { return x; } - -// CHECK-LABEL: define dso_local i64 @f_scalar_4 -// CHECK-SAME: (i64 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int64_t f_scalar_4(int64_t x) { return x; } - -#ifdef __SIZEOF_INT128__ -// CHECK-FORCEINT128-LABEL: define dso_local i128 @f_scalar_5 -// CHECK-FORCEINT128-SAME: (i128 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK-FORCEINT128: entry: -// -__int128_t f_scalar_5(__int128_t x) { return x; } -#endif - -// CHECK-LABEL: define dso_local float @f_fp_scalar_1 -// CHECK-SAME: (float noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -float f_fp_scalar_1(float x) { return x; } - -// CHECK-LABEL: define dso_local double @f_fp_scalar_2 -// CHECK-SAME: (double noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -double f_fp_scalar_2(double x) { return x; } - -// Scalars larger than 2*xlen are passed/returned indirect. However, the -// RISC-V LLVM backend can handle this fine, so the function doesn't need to -// be modified. - -// CHECK-LABEL: define dso_local fp128 @f_fp_scalar_3 -// CHECK-SAME: (fp128 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -long double f_fp_scalar_3(long double x) { return x; } - -// Empty structs or unions are ignored. - -struct empty_s {}; - -// CHECK-LABEL: define dso_local void @f_agg_empty_struct -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct empty_s f_agg_empty_struct(struct empty_s x) { - return x; -} - -union empty_u {}; - -// CHECK-LABEL: define dso_local void @f_agg_empty_union -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union empty_u f_agg_empty_union(union empty_u x) { - return x; -} - -// Aggregates <= 2*xlen may be passed in registers, so will be coerced to -// integer arguments. The rules for return are the same. - -struct tiny { - uint8_t a, b, c, d; -}; - -// CHECK-LABEL: define dso_local void @f_agg_tiny -// CHECK-SAME: (i32 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_tiny(struct tiny x) { - x.a += x.b; - x.c += x.d; -} - -// CHECK-LABEL: define dso_local i32 @f_agg_tiny_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct tiny f_agg_tiny_ret(void) { - return (struct tiny){1, 2, 3, 4}; -} - -typedef uint8_t v4i8 __attribute__((vector_size(4))); -typedef int32_t v1i32 __attribute__((vector_size(4))); - -// CHECK-LABEL: define dso_local void @f_vec_tiny_v4i8 -// CHECK-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_tiny_v4i8(v4i8 x) { - x[0] = x[1]; - x[2] = x[3]; -} - -// CHECK-LABEL: define dso_local i32 @f_vec_tiny_v4i8_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v4i8 f_vec_tiny_v4i8_ret(void) { - return (v4i8){1, 2, 3, 4}; -} - -// CHECK-LABEL: define dso_local void @f_vec_tiny_v1i32 -// CHECK-SAME: (i32 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_tiny_v1i32(v1i32 x) { - x[0] = 114; -} - -// CHECK-LABEL: define dso_local i32 @f_vec_tiny_v1i32_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v1i32 f_vec_tiny_v1i32_ret(void) { - return (v1i32){1}; -} - -struct small { - int32_t a, *b; -}; - -// CHECK-LABEL: define dso_local void @f_agg_small -// CHECK-SAME: ([2 x i32] [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_small(struct small x) { - x.a += *x.b; - x.b = &x.a; -} - -// CHECK-LABEL: define dso_local [2 x i32] @f_agg_small_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct small f_agg_small_ret(void) { - return (struct small){1, 0}; -} - -typedef uint8_t v8i8 __attribute__((vector_size(8))); -typedef int64_t v1i64 __attribute__((vector_size(8))); - -// CHECK-LABEL: define dso_local void @f_vec_small_v8i8 -// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_small_v8i8(v8i8 x) { - x[0] = x[7]; -} - -// CHECK-LABEL: define dso_local i64 @f_vec_small_v8i8_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v8i8 f_vec_small_v8i8_ret(void) { - return (v8i8){1, 2, 3, 4, 5, 6, 7, 8}; -} - -// CHECK-LABEL: define dso_local void @f_vec_small_v1i64 -// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_small_v1i64(v1i64 x) { - x[0] = 114; -} - -// CHECK-LABEL: define dso_local i64 @f_vec_small_v1i64_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v1i64 f_vec_small_v1i64_ret(void) { - return (v1i64){1}; -} - -// Aggregates of 2*xlen size and 2*xlen alignment should be coerced to a -// single 2*xlen-sized argument, to ensure that alignment can be maintained if -// passed on the stack. - -struct small_aligned { - int64_t a; -}; - -// CHECK-LABEL: define dso_local void @f_agg_small_aligned -// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_small_aligned(struct small_aligned x) { - x.a += x.a; -} - -// CHECK-LABEL: define dso_local i64 @f_agg_small_aligned_ret -// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { - return (struct small_aligned){10}; -} - -// Aggregates greater > 2*xlen will be passed and returned indirectly -struct large { - int32_t a, b, c, d; -}; - -// CHECK-LABEL: define dso_local void @f_agg_large -// CHECK-SAME: (ptr noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_large(struct large x) { - x.a = x.b + x.c + x.d; -} - -// The address where the struct should be written to will be the first -// argument -// CHECK-LABEL: define dso_local void @f_agg_large_ret -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[I:%.*]], i8 noundef signext [[J:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_agg_large_ret(int32_t i, int8_t j) { - return (struct large){1, 2, 3, 4}; -} - -typedef unsigned char v16i8 __attribute__((vector_size(16))); - -// CHECK-LABEL: define dso_local void @f_vec_large_v16i8 -// CHECK-SAME: (ptr noundef [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_large_v16i8(v16i8 x) { - x[0] = x[7]; -} - -// CHECK-LABEL: define dso_local void @f_vec_large_v16i8_ret -// CHECK-SAME: (ptr noalias sret(<16 x i8>) align 16 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -v16i8 f_vec_large_v16i8_ret(void) { - return (v16i8){1, 2, 3, 4, 5, 6, 7, 8}; -} - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers - -// CHECK-LABEL: define dso_local i32 @f_scalar_stack_1 -// CHECK-SAME: (i32 [[A_COERCE:%.*]], [2 x i32] [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, - struct large d, uint8_t e, int8_t f, uint8_t g, int8_t h) { - return g + h; -} - -// Ensure that scalars passed on the stack are still determined correctly in -// the presence of large return values that consume a register due to the need -// to pass a pointer. - -// CHECK-LABEL: define dso_local void @f_scalar_stack_2 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 4 [[AGG_RESULT:%.*]], i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_2(int32_t a, int64_t b, int64_t c, long double d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} - -// CHECK-LABEL: define dso_local fp128 @f_scalar_stack_4 -// CHECK-SAME: (i32 noundef [[A:%.*]], i64 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -long double f_scalar_stack_4(int32_t a, int64_t b, int64_t c, long double d, - uint8_t e, int8_t f, uint8_t g) { - return d; -} - -// Aggregates and >=XLen scalars passed on the stack should be lowered just as -// they would be if passed via registers. - -// CHECK-LABEL: define dso_local void @f_scalar_stack_5 -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 noundef [[E:%.*]], i64 noundef [[F:%.*]], float noundef [[G:%.*]], double noundef [[H:%.*]], fp128 noundef [[I:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_scalar_stack_5(double a, int64_t b, double c, int64_t d, int e, - int64_t f, float g, double h, long double i) {} - -// CHECK-LABEL: define dso_local void @f_agg_stack -// CHECK-SAME: (double noundef [[A:%.*]], i64 noundef [[B:%.*]], double noundef [[C:%.*]], i64 noundef [[D:%.*]], i32 [[E_COERCE:%.*]], [2 x i32] [[F_COERCE:%.*]], i64 [[G_COERCE:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_stack(double a, int64_t b, double c, int64_t d, struct tiny e, - struct small f, struct small_aligned g, struct large h) {} Index: clang/test/CodeGen/RISCV/riscv32-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32d-abi.c +++ /dev/null @@ -1,466 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -#include - -// Verify that the tracking of used GPRs and FPRs works correctly by checking -// that small integers are sign/zero extended when passed in registers. - -// Doubles are passed in FPRs, so argument 'i' will be passed zero-extended -// because it will be passed in a GPR. - -// CHECK-LABEL: define dso_local void @f_fpr_tracking -// CHECK-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_fpr_tracking(double a, double b, double c, double d, double e, double f, - double g, double h, uint8_t i) {} - -// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will -// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are -// available the widths are <= XLEN and FLEN, and should be expanded to -// separate arguments in IR. They are passed by the same rules for returns, -// but will be lowered to simple two-element structs if necessary (as LLVM IR -// functions cannot return multiple values). - -// A struct containing just one floating-point real is passed as though it -// were a standalone floating-point real. - -struct double_s { double f; }; - -// CHECK-LABEL: define dso_local void @f_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_s_arg(struct double_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_s f_ret_double_s(void) { - return (struct double_s){1.0}; -} - -// A struct containing a double and any number of zero-width bitfields is -// passed as though it were a standalone floating-point real. - -struct zbf_double_s { int : 0; double f; }; -struct zbf_double_zbf_s { int : 0; double f; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_zbf_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_double_s_arg(struct zbf_double_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_zbf_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_double_s f_ret_zbf_double_s(void) { - return (struct zbf_double_s){1.0}; -} - -// CHECK-LABEL: define dso_local void @f_zbf_double_zbf_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_zbf_double_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_double_zbf_s f_ret_zbf_double_zbf_s(void) { - return (struct zbf_double_zbf_s){1.0}; -} - -// Check that structs containing two floating point values (FLEN <= width) are -// expanded provided sufficient FPRs are available. - -struct double_double_s { double f; double g; }; -struct double_float_s { double f; float g; }; - -// CHECK-LABEL: define dso_local void @f_double_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_double_s_arg(struct double_double_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_double_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_double_s f_ret_double_double_s(void) { - return (struct double_double_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_double_float_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_float_s_arg(struct double_float_s a) {} - -// CHECK-LABEL: define dso_local { double, float } @f_ret_double_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_float_s f_ret_double_float_s(void) { - return (struct double_float_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_double_double_s_arg_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], ptr noundef [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d, - double e, double f, double g, struct double_double_s h) {} - -// Check that structs containing int+double values are expanded, provided -// sufficient FPRs and GPRs are available. The integer components are neither -// sign or zero-extended. - -struct double_int8_s { double f; int8_t i; }; -struct double_uint8_s { double f; uint8_t i; }; -struct double_int32_s { double f; int32_t i; }; -struct double_int64_s { double f; int64_t i; }; -struct double_int64bf_s { double f; int64_t i : 32; }; -struct double_int8_zbf_s { double f; int8_t i; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_double_int8_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_s_arg(struct double_int8_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int8_s f_ret_double_int8_s(void) { - return (struct double_int8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_uint8_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_uint8_s_arg(struct double_uint8_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_uint8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_uint8_s f_ret_double_uint8_s(void) { - return (struct double_uint8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int32_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int32_s_arg(struct double_int32_s a) {} - -// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int32_s f_ret_double_int32_s(void) { - return (struct double_int32_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int64_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int64_s_arg(struct double_int64_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_double_int64_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_DOUBLE_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct double_int64_s f_ret_double_int64_s(void) { - return (struct double_int64_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int64bf_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int64bf_s_arg(struct double_int64bf_s a) {} - -// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int64bf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int64bf_s f_ret_double_int64bf_s(void) { - return (struct double_int64bf_s){1.0, 2}; -} - -// The zero-width bitfield means the struct can't be passed according to the -// floating point calling convention. - -// CHECK-LABEL: define dso_local void @f_double_int8_zbf_s -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_zbf_s(struct double_int8_zbf_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int8_zbf_s f_ret_double_int8_zbf_s(void) { - return (struct double_int8_zbf_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs -// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, - int f, int g, int h, struct double_int8_s i) {} - -// CHECK-LABEL: define dso_local void @f_struct_double_int8_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], ptr noundef [[I:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d, - double e, double f, double g, double h, struct double_int8_s i) {} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed as if it were an fp+fp struct. - -// CHECK-LABEL: define dso_local void @f_doublecomplex -// CHECK-SAME: (double noundef [[A_COERCE0:%.*]], double noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublecomplex(double __complex__ a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -double __complex__ f_ret_doublecomplex(void) { - return 1.0; -} - -struct doublecomplex_s { double __complex__ c; }; - -// CHECK-LABEL: define dso_local void @f_doublecomplex_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublecomplex_s_arg(struct doublecomplex_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublecomplex_s f_ret_doublecomplex_s(void) { - return (struct doublecomplex_s){1.0}; -} - -// Test single or two-element structs that need flattening. e.g. those -// containing nested structs, doubles in small arrays, zero-length structs etc. - -struct doublearr1_s { double a[1]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr1_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr1_s_arg(struct doublearr1_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_doublearr1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr1_s f_ret_doublearr1_s(void) { - return (struct doublearr1_s){{1.0}}; -} - -struct doublearr2_s { double a[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_s_arg(struct doublearr2_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_s f_ret_doublearr2_s(void) { - return (struct doublearr2_s){{1.0, 2.0}}; -} - -struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s(void) { - return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s(void) { - return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky3_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s(void) { - return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky4_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s(void) { - return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; -} - -// Test structs that should be passed according to the normal integer calling -// convention. - -struct int_double_int_s { int a; double b; int c; }; - -// CHECK-LABEL: define dso_local void @f_int_double_int_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int_double_int_s_arg(struct int_double_int_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int_double_int_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_INT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int_double_int_s f_ret_int_double_int_s(void) { - return (struct int_double_int_s){1, 2.0, 3}; -} - -struct int64_double_s { int64_t a; double b; }; - -// CHECK-LABEL: define dso_local void @f_int64_double_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int64_double_s_arg(struct int64_double_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int64_double_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT64_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int64_double_s f_ret_int64_double_s(void) { - return (struct int64_double_s){1, 2.0}; -} - -struct char_char_double_s { char a; char b; double c; }; - -// CHECK-LABEL: define dso_local void @f_char_char_double_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_char_char_double_s_arg(struct char_char_double_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_char_char_double_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_CHAR_CHAR_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct char_char_double_s f_ret_char_char_double_s(void) { - return (struct char_char_double_s){1, 2, 3.0}; -} - -// Unions are always passed according to the integer calling convention, even -// if they can only contain a double. - -union double_u { double a; }; - -// CHECK-LABEL: define dso_local void @f_double_u_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_u_arg(union double_u a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_double_u -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union double_u f_ret_double_u(void) { - return (union double_u){1.0}; -} - -// Test that we don't incorrectly think double+int/double+double structs will -// be returned indirectly and thus have an off-by-one error for the number of -// GPRs available (this is an edge case when structs > 2*XLEN are still -// returned in registers). This includes complex doubles, which are treated as -// double+double structs by the ABI. - -// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s_double_int32_s_just_sufficient_gprs -// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct double_int32_s f_ret_double_int32_s_double_int32_s_just_sufficient_gprs( - int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { - return (struct double_int32_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_double_double_s_double_int32_s_just_sufficient_gprs -// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct double_double_s f_ret_double_double_s_double_int32_s_just_sufficient_gprs( - int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { - return (struct double_double_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex_double_int32_s_just_sufficient_gprs -// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -double __complex__ f_ret_doublecomplex_double_int32_s_just_sufficient_gprs( - int a, int b, int c, int d, int e, int f, int g, struct double_int32_s h) { - return 1.0; -} Index: clang/test/CodeGen/RISCV/riscv32-ilp32f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32f-abi.c +++ /dev/null @@ -1,64 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -#include - -// CHECK-LABEL: define dso_local void @f_fpr_tracking -// CHECK-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], i8 noundef signext [[E:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_fpr_tracking(double a, double b, double c, double d, int8_t e) {} - -// Lowering for doubles is unnmodified, as 64 > FLEN. - -struct double_s { double d; }; - -// CHECK-LABEL: define dso_local void @f_double_s_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_s_arg(struct double_s a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_s f_ret_double_s(void) { - return (struct double_s){1.0}; -} - -struct double_double_s { double d; double e; }; - -// CHECK-LABEL: define dso_local void @f_double_double_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_double_s_arg(struct double_double_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_double_double_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_DOUBLE_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct double_double_s f_ret_double_double_s(void) { - return (struct double_double_s){1.0, 2.0}; -} - -struct double_int8_s { double d; int64_t i; }; - -struct int_double_s { int a; double b; }; - -// CHECK-LABEL: define dso_local void @f_int_double_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int_double_s_arg(struct int_double_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int_double_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int_double_s f_ret_int_double_s(void) { - return (struct int_double_s){1, 2.0}; -} - Index: clang/test/CodeGen/RISCV/riscv32-ilp32f-ilp32d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv32-ilp32f-ilp32d-abi.c +++ /dev/null @@ -1,420 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv32 -target-feature +f -target-abi ilp32f -emit-llvm %s -o - \ -// RUN: | FileCheck %s -// RUN: %clang_cc1 -triple riscv32 -target-feature +d -target-feature +f -target-abi ilp32d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -#include - -// Verify that the tracking of used GPRs and FPRs works correctly by checking -// that small integers are sign/zero extended when passed in registers. - -// Floats are passed in FPRs, so argument 'i' will be passed zero-extended -// because it will be passed in a GPR. - -// CHECK-LABEL: define dso_local void @f_fpr_tracking -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_fpr_tracking(float a, float b, float c, float d, float e, float f, - float g, float h, uint8_t i) {} - -// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will -// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are -// available the widths are <= XLEN and FLEN, and should be expanded to -// separate arguments in IR. They are passed by the same rules for returns, -// but will be lowered to simple two-element structs if necessary (as LLVM IR -// functions cannot return multiple values). - -// A struct containing just one floating-point real is passed as though it -// were a standalone floating-point real. - -struct float_s { float f; }; - -// CHECK-LABEL: define dso_local void @f_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_s_arg(struct float_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_s f_ret_float_s(void) { - return (struct float_s){1.0}; -} - -// A struct containing a float and any number of zero-width bitfields is -// passed as though it were a standalone floating-point real. - -struct zbf_float_s { int : 0; float f; }; -struct zbf_float_zbf_s { int : 0; float f; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_zbf_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_float_s_arg(struct zbf_float_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_zbf_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_float_s f_ret_zbf_float_s(void) { - return (struct zbf_float_s){1.0}; -} - -// CHECK-LABEL: define dso_local void @f_zbf_float_zbf_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_zbf_float_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_float_zbf_s f_ret_zbf_float_zbf_s(void) { - return (struct zbf_float_zbf_s){1.0}; -} - -// Check that structs containing two float values (FLEN <= width) are expanded -// provided sufficient FPRs are available. - -struct float_float_s { float f; float g; }; - -// CHECK-LABEL: define dso_local void @f_float_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_float_s_arg(struct float_float_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_float_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_float_s f_ret_float_float_s(void) { - return (struct float_float_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_float_float_s_arg_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], [2 x i32] [[H_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, - float e, float f, float g, struct float_float_s h) {} - -// Check that structs containing int+float values are expanded, provided -// sufficient FPRs and GPRs are available. The integer components are neither -// sign or zero-extended. - -struct float_int8_s { float f; int8_t i; }; -struct float_uint8_s { float f; uint8_t i; }; -struct float_int32_s { float f; int32_t i; }; -struct float_int64_s { float f; int64_t i; }; -struct float_int64bf_s { float f; int64_t i : 32; }; -struct float_int8_zbf_s { float f; int8_t i; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_float_int8_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_s_arg(struct float_int8_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int8_s f_ret_float_int8_s(void) { - return (struct float_int8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_uint8_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_uint8_s_arg(struct float_uint8_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_uint8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_uint8_s f_ret_float_uint8_s(void) { - return (struct float_uint8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int32_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int32_s_arg(struct float_int32_s a) {} - -// CHECK-LABEL: define dso_local { float, i32 } @f_ret_float_int32_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int32_s f_ret_float_int32_s(void) { - return (struct float_int32_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int64_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int64_s_arg(struct float_int64_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_float_int64_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_FLOAT_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct float_int64_s f_ret_float_int64_s(void) { - return (struct float_int64_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int64bf_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int64bf_s_arg(struct float_int64bf_s a) {} - -// CHECK-LABEL: define dso_local { float, i32 } @f_ret_float_int64bf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int64bf_s f_ret_float_int64bf_s(void) { - return (struct float_int64bf_s){1.0, 2}; -} - -// The zero-width bitfield means the struct can't be passed according to the -// floating point calling convention. - -// CHECK-LABEL: define dso_local void @f_float_int8_zbf_s -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_zbf_s(struct float_int8_zbf_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int8_zbf_s f_ret_float_int8_zbf_s(void) { - return (struct float_int8_zbf_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs -// CHECK-SAME: (i32 noundef [[A:%.*]], i32 noundef [[B:%.*]], i32 noundef [[C:%.*]], i32 noundef [[D:%.*]], i32 noundef [[E:%.*]], i32 noundef [[F:%.*]], i32 noundef [[G:%.*]], i32 noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, - int f, int g, int h, struct float_int8_s i) {} - -// CHECK-LABEL: define dso_local void @f_struct_float_int8_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], [2 x i32] [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d, - float e, float f, float g, float h, struct float_int8_s i) {} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed as if it were an fp+fp struct. - -// CHECK-LABEL: define dso_local void @f_floatcomplex -// CHECK-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex(float __complex__ a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -float __complex__ f_ret_floatcomplex(void) { - return 1.0; -} - -struct floatcomplex_s { float __complex__ c; }; - -// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_s_arg(struct floatcomplex_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatcomplex_s f_ret_floatcomplex_s(void) { - return (struct floatcomplex_s){1.0}; -} - -// Test single or two-element structs that need flattening. e.g. those -// containing nested structs, floats in small arrays, zero-length structs etc. - -struct floatarr1_s { float a[1]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr1_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr1_s_arg(struct floatarr1_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_floatarr1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr1_s f_ret_floatarr1_s(void) { - return (struct floatarr1_s){{1.0}}; -} - -struct floatarr2_s { float a[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_s_arg(struct floatarr2_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_s f_ret_floatarr2_s(void) { - return (struct floatarr2_s){{1.0, 2.0}}; -} - -struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s(void) { - return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s(void) { - return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky3_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s(void) { - return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky4_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s(void) { - return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; -} - -// Test structs that should be passed according to the normal integer calling -// convention. - -struct int_float_int_s { int a; float b; int c; }; - -// CHECK-LABEL: define dso_local void @f_int_float_int_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int_float_int_s_arg(struct int_float_int_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int_float_int_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_FLOAT_INT_S:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int_float_int_s f_ret_int_float_int_s(void) { - return (struct int_float_int_s){1, 2.0, 3}; -} - -struct int64_float_s { int64_t a; float b; }; - -// CHECK-LABEL: define dso_local void @f_int64_float_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int64_float_s_arg(struct int64_float_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int64_float_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT64_FLOAT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int64_float_s f_ret_int64_float_s(void) { - return (struct int64_float_s){1, 2.0}; -} - -struct char_char_float_s { char a; char b; float c; }; - -// CHECK-LABEL: define dso_local void @f_char_char_float_s_arg -// CHECK-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_char_char_float_s_arg(struct char_char_float_s a) {} - -// CHECK-LABEL: define dso_local [2 x i32] @f_ret_char_char_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct char_char_float_s f_ret_char_char_float_s(void) { - return (struct char_char_float_s){1, 2, 3.0}; -} - -// Unions are always passed according to the integer calling convention, even -// if they can only contain a float. - -union float_u { float a; }; - -// CHECK-LABEL: define dso_local void @f_float_u_arg -// CHECK-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_u_arg(union float_u a) {} - -// CHECK-LABEL: define dso_local i32 @f_ret_float_u -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union float_u f_ret_float_u(void) { - return (union float_u){1.0}; -} Index: clang/test/CodeGen/RISCV/riscv32-int128-abi.c =================================================================== --- /dev/null +++ clang/test/CodeGen/RISCV/riscv32-int128-abi.c @@ -0,0 +1,9 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 +// RUN: %clang_cc1 -triple riscv32 -emit-llvm -fforce-enable-int128 %s -o - \ +// RUN: | FileCheck %s + +// CHECK-LABEL: define dso_local i128 @f_int128 +// CHECK-SAME: (i128 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK: entry: +// +__int128_t f_int128(__int128_t x) { return x; } Index: clang/test/CodeGen/RISCV/riscv64-abi.c =================================================================== --- /dev/null +++ clang/test/CodeGen/RISCV/riscv64-abi.c @@ -0,0 +1,1483 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 +// RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64-LP64F,LP64 %s +// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64F-LP64D,LP64-LP64F,LP64F %s +// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-feature +d -target-abi lp64d -emit-llvm %s -o - \ +// RUN: | FileCheck -check-prefixes=LP64-LP64F-LP64D,LP64F-LP64D,LP64D %s + +#include +#include + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_void +// LP64-LP64F-LP64D-SAME: () #[[ATTR0:[0-9]+]] { +// LP64-LP64F-LP64D: entry: +// +void f_void(void) {} + +// Scalar arguments and return values smaller than the word size are extended +// according to the sign of their type, up to 32 bits + +// LP64-LP64F-LP64D-LABEL: define dso_local zeroext i1 @f_scalar_0 +// LP64-LP64F-LP64D-SAME: (i1 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +_Bool f_scalar_0(_Bool x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i8 @f_scalar_1 +// LP64-LP64F-LP64D-SAME: (i8 noundef signext [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int8_t f_scalar_1(int8_t x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local zeroext i8 @f_scalar_2 +// LP64-LP64F-LP64D-SAME: (i8 noundef zeroext [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +uint8_t f_scalar_2(uint8_t x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i32 @f_scalar_3 +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +uint32_t f_scalar_3(int32_t x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_scalar_4 +// LP64-LP64F-LP64D-SAME: (i64 noundef [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int64_t f_scalar_4(int64_t x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local float @f_fp_scalar_1 +// LP64-LP64F-LP64D-SAME: (float noundef [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +float f_fp_scalar_1(float x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local double @f_fp_scalar_2 +// LP64-LP64F-LP64D-SAME: (double noundef [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +double f_fp_scalar_2(double x) { return x; } + +// LP64-LP64F-LP64D-LABEL: define dso_local fp128 @f_fp_scalar_3 +// LP64-LP64F-LP64D-SAME: (fp128 noundef [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +long double f_fp_scalar_3(long double x) { return x; } + +// Empty structs or unions are ignored. + +struct empty_s {}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_empty_struct +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct empty_s f_agg_empty_struct(struct empty_s x) { + return x; +} + +union empty_u {}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_empty_union +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +union empty_u f_agg_empty_union(union empty_u x) { + return x; +} + +// Aggregates <= 2*xlen may be passed in registers, so will be coerced to +// integer arguments. The rules for return are the same. + +struct tiny { + uint16_t a, b, c, d; +}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_tiny +// LP64-LP64F-LP64D-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_agg_tiny(struct tiny x) { + x.a += x.b; + x.c += x.d; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_agg_tiny_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct tiny f_agg_tiny_ret(void) { + return (struct tiny){1, 2, 3, 4}; +} + +typedef uint16_t v4i16 __attribute__((vector_size(8))); +typedef int64_t v1i64 __attribute__((vector_size(8))); + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_tiny_v4i16 +// LP64-LP64F-LP64D-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_vec_tiny_v4i16(v4i16 x) { + x[0] = x[1]; + x[2] = x[3]; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_vec_tiny_v4i16_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +v4i16 f_vec_tiny_v4i16_ret(void) { + return (v4i16){1, 2, 3, 4}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_tiny_v1i64 +// LP64-LP64F-LP64D-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_vec_tiny_v1i64(v1i64 x) { + x[0] = 114; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_vec_tiny_v1i64_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +v1i64 f_vec_tiny_v1i64_ret(void) { + return (v1i64){1}; +} + +struct small { + int64_t a, *b; +}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_small +// LP64-LP64F-LP64D-SAME: ([2 x i64] [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_agg_small(struct small x) { + x.a += *x.b; + x.b = &x.a; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local [2 x i64] @f_agg_small_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct small f_agg_small_ret(void) { + return (struct small){1, 0}; +} + +typedef uint16_t v8i16 __attribute__((vector_size(16))); +typedef __int128_t v1i128 __attribute__((vector_size(16))); + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_small_v8i16 +// LP64-LP64F-LP64D-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_vec_small_v8i16(v8i16 x) { + x[0] = x[7]; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i128 @f_vec_small_v8i16_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +v8i16 f_vec_small_v8i16_ret(void) { + return (v8i16){1, 2, 3, 4, 5, 6, 7, 8}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_small_v1i128 +// LP64-LP64F-LP64D-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_vec_small_v1i128(v1i128 x) { + x[0] = 114; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i128 @f_vec_small_v1i128_ret +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +v1i128 f_vec_small_v1i128_ret(void) { + return (v1i128){1}; +} + +// Aggregates of 2*xlen size and 2*xlen alignment should be coerced to a +// single 2*xlen-sized argument, to ensure that alignment can be maintained if +// passed on the stack. + +struct small_aligned { + __int128_t a; +}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_small_aligned +// LP64-LP64F-LP64D-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_agg_small_aligned(struct small_aligned x) { + x.a += x.a; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local i128 @f_agg_small_aligned_ret +// LP64-LP64F-LP64D-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { + return (struct small_aligned){10}; +} + +// Aggregates greater > 2*xlen will be passed and returned indirectly +struct large { + int64_t a, b, c, d; +}; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_large +// LP64-LP64F-LP64D-SAME: (ptr noundef [[X:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_agg_large(struct large x) { + x.a = x.b + x.c + x.d; +} + +// The address where the struct should be written to will be the first +// argument +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_agg_large_ret +// LP64-LP64F-LP64D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[I:%.*]], i8 noundef signext [[J:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct large f_agg_large_ret(int32_t i, int8_t j) { + return (struct large){1, 2, 3, 4}; +} + +typedef unsigned char v32i8 __attribute__((vector_size(32))); + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_large_v32i8 +// LP64-LP64F-LP64D-SAME: (ptr noundef [[TMP0:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_vec_large_v32i8(v32i8 x) { + x[0] = x[7]; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_vec_large_v32i8_ret +// LP64-LP64F-LP64D-SAME: (ptr noalias sret(<32 x i8>) align 32 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +v32i8 f_vec_large_v32i8_ret(void) { + return (v32i8){1, 2, 3, 4, 5, 6, 7, 8}; +} + +// Scalars passed on the stack should have signext/zeroext attributes, just as +// if they were passed in registers. + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i32 @f_scalar_stack_1 +// LP64-LP64F-LP64D-SAME: (i64 [[A_COERCE:%.*]], [2 x i64] [[B_COERCE:%.*]], i128 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[H:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, + struct large d, uint8_t e, int8_t f, uint8_t g, int8_t h) { + return g + h; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i32 @f_scalar_stack_2 +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int f_scalar_stack_2(int32_t a, __int128_t b, int64_t c, long double d, v32i8 e, + uint8_t f, int8_t g, uint8_t h) { + return g + h; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i32 @f_scalar_stack_3 +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int f_scalar_stack_3(int32_t a, __int128_t b, double c, long double d, v32i8 e, + uint8_t f, int8_t g, uint8_t h) { + return g + h; +} + +// Ensure that scalars passed on the stack are still determined correctly in +// the presence of large return values that consume a register due to the need +// to pass a pointer. + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_scalar_stack_4 +// LP64-LP64F-LP64D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct large f_scalar_stack_4(uint32_t a, __int128_t b, long double c, v32i8 d, + uint8_t e, int8_t f, uint8_t g) { + return (struct large){a, e, f, g}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_scalar_stack_5 +// LP64-LP64F-LP64D-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], double noundef [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct large f_scalar_stack_5(double a, __int128_t b, long double c, v32i8 d, + uint8_t e, int8_t f, uint8_t g) { + return (struct large){a, e, f, g}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local signext i32 @f_scalar_stack_6 +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], float noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +int f_scalar_stack_6(int32_t a, __int128_t b, float c, long double d, v32i8 e, + uint8_t f, int8_t g, uint8_t h) { + return g + h; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_fpr_tracking +// LP64-LP64F-LP64D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_fpr_tracking(float a, float b, float c, float d, float e, float f, + float g, float h, uint8_t i) {} + +// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will +// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are +// available the widths are <= XLEN and FLEN, and should be expanded to +// separate arguments in IR. They are passed by the same rules for returns, +// but will be lowered to simple two-element structs if necessary (as LLVM IR +// functions cannot return multiple values). + +// A struct containing just one floating-point real is passed as though it +// were a standalone floating-point real. + +struct float_s { float f; }; + +// LP64-LABEL: define dso_local void @f_float_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_s_arg(struct float_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local float @f_ret_float_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_s f_ret_float_s(void) { + return (struct float_s){1.0}; +} + +// A struct containing a float and any number of zero-width bitfields is +// passed as though it were a standalone floating-point real. + +struct zbf_float_s { int : 0; float f; }; +struct zbf_float_zbf_s { int : 0; float f; int : 0; }; + +// LP64-LABEL: define dso_local void @f_zbf_float_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_zbf_float_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_zbf_float_s_arg(struct zbf_float_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_zbf_float_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local float @f_ret_zbf_float_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct zbf_float_s f_ret_zbf_float_s(void) { + return (struct zbf_float_s){1.0}; +} + +// LP64-LABEL: define dso_local void @f_zbf_float_zbf_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_zbf_float_zbf_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_zbf_float_zbf_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local float @f_ret_zbf_float_zbf_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct zbf_float_zbf_s f_ret_zbf_float_zbf_s(void) { + return (struct zbf_float_zbf_s){1.0}; +} + +// Check that structs containing two float values (FLEN <= width) are expanded +// provided sufficient FPRs are available. + +struct float_float_s { float f; float g; }; + +// LP64-LABEL: define dso_local void @f_float_float_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_float_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_float_s_arg(struct float_float_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_float_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_float_float_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_float_s f_ret_float_float_s(void) { + return (struct float_float_s){1.0, 2.0}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_float_float_s_arg_insufficient_fprs +// LP64-LP64F-LP64D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], i64 [[H_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, + float e, float f, float g, struct float_float_s h) {} + +// Check that structs containing int+float values are expanded, provided +// sufficient FPRs and GPRs are available. The integer components are neither +// sign or zero-extended. + +struct float_int8_s { float f; int8_t i; }; +struct float_uint8_s { float f; uint8_t i; }; +struct float_int32_s { float f; int32_t i; }; +struct float_int64_s { float f; int64_t i; }; +struct float_int128bf_s { float f; __int128_t i : 64; }; +struct float_int8_zbf_s { float f; int8_t i; int : 0; }; + +// LP64-LABEL: define dso_local void @f_float_int8_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_int8_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_int8_s_arg(struct float_int8_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_int8_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i8 } @f_ret_float_int8_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_int8_s f_ret_float_int8_s(void) { + return (struct float_int8_s){1.0, 2}; +} + +// LP64-LABEL: define dso_local void @f_float_uint8_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_uint8_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_uint8_s_arg(struct float_uint8_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_uint8_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i8 } @f_ret_float_uint8_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_uint8_s f_ret_float_uint8_s(void) { + return (struct float_uint8_s){1.0, 2}; +} + +// LP64-LABEL: define dso_local void @f_float_int32_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_int32_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_int32_s_arg(struct float_int32_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_int32_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i32 } @f_ret_float_int32_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_int32_s f_ret_float_int32_s(void) { + return (struct float_int32_s){1.0, 2}; +} + +// LP64-LABEL: define dso_local void @f_float_int64_s_arg +// LP64-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_int64_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_int64_s_arg(struct float_int64_s a) {} + +// LP64-LABEL: define dso_local [2 x i64] @f_ret_float_int64_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i64 } @f_ret_float_int64_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_int64_s f_ret_float_int64_s(void) { + return (struct float_int64_s){1.0, 2}; +} + +// LP64-LABEL: define dso_local void @f_float_int128bf_s_arg +// LP64-SAME: (i128 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_int128bf_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_int128bf_s_arg(struct float_int128bf_s a) {} + +// LP64-LABEL: define dso_local i128 @f_ret_float_int128bf_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local <{ float, i64 }> @f_ret_float_int128bf_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_int128bf_s f_ret_float_int128bf_s(void) { + return (struct float_int128bf_s){1.0, 2}; +} + +// The zero-width bitfield means the struct can't be passed according to the +// floating point calling convention. + +// LP64-LABEL: define dso_local void @f_float_int8_zbf_s +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_float_int8_zbf_s +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_float_int8_zbf_s(struct float_int8_zbf_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_float_int8_zbf_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, i8 } @f_ret_float_int8_zbf_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct float_int8_zbf_s f_ret_float_int8_zbf_s(void) { + return (struct float_int8_zbf_s){1.0, 2}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], i64 [[I_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, + int f, int g, int h, struct float_int8_s i) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_struct_float_int8_insufficient_fprs +// LP64-LP64F-LP64D-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i64 [[I_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d, + float e, float f, float g, float h, struct float_int8_s i) {} + +// Complex floating-point values or structs containing a single complex +// floating-point value should be passed as if it were an fp+fp struct. + +// LP64-LABEL: define dso_local void @f_floatcomplex +// LP64-SAME: (i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex +// LP64F-LP64D-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex(float __complex__ a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatcomplex +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatcomplex +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +float __complex__ f_ret_floatcomplex(void) { + return 1.0; +} + +struct floatcomplex_s { float __complex__ c; }; + +// LP64-LABEL: define dso_local void @f_floatcomplex_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex_s_arg(struct floatcomplex_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatcomplex_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatcomplex_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatcomplex_s f_ret_floatcomplex_s(void) { + return (struct floatcomplex_s){1.0}; +} + +// Complex floating-point values or structs containing a single complex +// floating-point value should be passed in GPRs if no two FPRs is available. + +// LP64-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs1 +// LP64-SAME: (i64 noundef [[A_COERCE:%.*]], i64 noundef [[B_COERCE:%.*]], i64 noundef [[C_COERCE:%.*]], i64 noundef [[D_COERCE:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs1 +// LP64F-LP64D-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex_insufficient_fprs1(float __complex__ a, float __complex__ b, + float __complex__ c, float __complex__ d, + float __complex__ e) {} + + +// LP64-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs1 +// LP64-SAME: (i64 [[A_COERCE:%.*]], i64 [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], i64 [[D_COERCE:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs1 +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], float [[TMP6:%.*]], float [[TMP7:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex_s_arg_insufficient_fprs1(struct floatcomplex_s a, + struct floatcomplex_s b, + struct floatcomplex_s c, + struct floatcomplex_s d, + struct floatcomplex_s e) {} + +// LP64-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs2 +// LP64-SAME: (float noundef [[A:%.*]], i64 noundef [[B_COERCE:%.*]], i64 noundef [[C_COERCE:%.*]], i64 noundef [[D_COERCE:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs2 +// LP64F-LP64D-SAME: (float noundef [[A:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex_insufficient_fprs2(float a, + float __complex__ b, float __complex__ c, + float __complex__ d, float __complex__ e) {} + + +// LP64-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs2 +// LP64-SAME: (float noundef [[A:%.*]], i64 [[B_COERCE:%.*]], i64 [[C_COERCE:%.*]], i64 [[D_COERCE:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs2 +// LP64F-LP64D-SAME: (float noundef [[A:%.*]], float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatcomplex_s_arg_insufficient_fprs2(float a, + struct floatcomplex_s b, + struct floatcomplex_s c, + struct floatcomplex_s d, + struct floatcomplex_s e) {} + +// Test single or two-element structs that need flattening. e.g. those +// containing nested structs, floats in small arrays, zero-length structs etc. + +struct floatarr1_s { float a[1]; }; + +// LP64-LABEL: define dso_local void @f_floatarr1_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr1_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr1_s_arg(struct floatarr1_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr1_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local float @f_ret_floatarr1_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr1_s f_ret_floatarr1_s(void) { + return (struct floatarr1_s){{1.0}}; +} + +struct floatarr2_s { float a[2]; }; + +// LP64-LABEL: define dso_local void @f_floatarr2_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr2_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr2_s_arg(struct floatarr2_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr2_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatarr2_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr2_s f_ret_floatarr2_s(void) { + return (struct floatarr2_s){{1.0, 2.0}}; +} + +struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; }; + +// LP64-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr2_tricky1_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky1_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s(void) { + return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; }; + +// LP64-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr2_tricky2_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky2_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s(void) { + return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; }; + +// LP64-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr2_tricky3_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky3_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s(void) { + return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; }; + +// LP64-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg +// LP64-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg +// LP64F-LP64D-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64F-LP64D: entry: +// +void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {} + +// LP64-LABEL: define dso_local i64 @f_ret_floatarr2_tricky4_s +// LP64-SAME: () #[[ATTR0]] { +// LP64: entry: +// +// LP64F-LP64D-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky4_s +// LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64F-LP64D: entry: +// +struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s(void) { + return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; +} + +// Test structs that should be passed according to the normal integer calling +// convention. + +struct int_float_int_s { int a; float b; int c; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_int_float_int_s_arg +// LP64-LP64F-LP64D-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_int_float_int_s_arg(struct int_float_int_s a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local [2 x i64] @f_ret_int_float_int_s +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct int_float_int_s f_ret_int_float_int_s(void) { + return (struct int_float_int_s){1, 2.0, 3}; +} + +struct char_char_float_s { char a; char b; float c; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_char_char_float_s_arg +// LP64-LP64F-LP64D-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_char_char_float_s_arg(struct char_char_float_s a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_ret_char_char_float_s +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct char_char_float_s f_ret_char_char_float_s(void) { + return (struct char_char_float_s){1, 2, 3.0}; +} + +// Unions are always passed according to the integer calling convention, even +// if they can only contain a float. + +union float_u { float a; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_float_u_arg +// LP64-LP64F-LP64D-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_float_u_arg(union float_u a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_ret_float_u +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +union float_u f_ret_float_u(void) { + return (union float_u){1.0}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_fpr_tracking2 +// LP64-LP64F-LP64D-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_fpr_tracking2(double a, double b, double c, double d, double e, double f, + double g, double h, uint8_t i) {} + +// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will +// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are +// available the widths are <= XLEN and FLEN, and should be expanded to +// separate arguments in IR. They are passed by the same rules for returns, +// but will be lowered to simple two-element structs if necessary (as LLVM IR +// functions cannot return multiple values). + +// A struct containing just one floating-point real is passed as though it +// were a standalone floating-point real. + +struct double_s { double f; }; + +// LP64-LP64F-LABEL: define dso_local void @f_double_s_arg +// LP64-LP64F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_s_arg +// LP64D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_s_arg(struct double_s a) {} + +// LP64-LP64F-LABEL: define dso_local i64 @f_ret_double_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local double @f_ret_double_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_s f_ret_double_s(void) { + return (struct double_s){1.0}; +} + +// A struct containing a double and any number of zero-width bitfields is +// passed as though it were a standalone floating-point real. + +struct zbf_double_s { int : 0; double f; }; +struct zbf_double_zbf_s { int : 0; double f; int : 0; }; + +// LP64-LP64F-LABEL: define dso_local void @f_zbf_double_s_arg +// LP64-LP64F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_zbf_double_s_arg +// LP64D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_zbf_double_s_arg(struct zbf_double_s a) {} + +// LP64-LP64F-LABEL: define dso_local i64 @f_ret_zbf_double_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local double @f_ret_zbf_double_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct zbf_double_s f_ret_zbf_double_s(void) { + return (struct zbf_double_s){1.0}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_zbf_double_zbf_s_arg +// LP64-LP64F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_zbf_double_zbf_s_arg +// LP64D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {} + +// LP64-LP64F-LABEL: define dso_local i64 @f_ret_zbf_double_zbf_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local double @f_ret_zbf_double_zbf_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct zbf_double_zbf_s f_ret_zbf_double_zbf_s(void) { + return (struct zbf_double_zbf_s){1.0}; +} + +// Check that structs containing two floating point values (FLEN <= width) are +// expanded provided sufficient FPRs are available. + +struct double_double_s { double f; double g; }; +struct double_float_s { double f; float g; }; + +// LP64-LP64F-LABEL: define dso_local void @f_double_double_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_double_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_double_s_arg(struct double_double_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_double_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_double_double_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_double_s f_ret_double_double_s(void) { + return (struct double_double_s){1.0, 2.0}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_double_float_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_float_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_float_s_arg(struct double_float_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_float_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, float } @f_ret_double_float_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_float_s f_ret_double_float_s(void) { + return (struct double_float_s){1.0, 2.0}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_double_double_s_arg_insufficient_fprs +// LP64-LP64F-LP64D-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], [2 x i64] [[H_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d, + double e, double f, double g, struct double_double_s h) {} + +// Check that structs containing int+double values are expanded, provided +// sufficient FPRs and GPRs are available. The integer components are neither +// sign or zero-extended. + +struct double_int8_s { double f; int8_t i; }; +struct double_uint8_s { double f; uint8_t i; }; +struct double_int32_s { double f; int32_t i; }; +struct double_int64_s { double f; int64_t i; }; +struct double_int128bf_s { double f; __int128_t i : 64; }; +struct double_int8_zbf_s { double f; int8_t i; int : 0; }; + +// LP64-LP64F-LABEL: define dso_local void @f_double_int8_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_int8_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_int8_s_arg(struct double_int8_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_int8_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i8 } @f_ret_double_int8_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_int8_s f_ret_double_int8_s(void) { + return (struct double_int8_s){1.0, 2}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_double_uint8_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_uint8_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_uint8_s_arg(struct double_uint8_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_uint8_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i8 } @f_ret_double_uint8_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_uint8_s f_ret_double_uint8_s(void) { + return (struct double_uint8_s){1.0, 2}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_double_int32_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_int32_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_int32_s_arg(struct double_int32_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_int32_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_int32_s f_ret_double_int32_s(void) { + return (struct double_int32_s){1.0, 2}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_double_int64_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_int64_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_int64_s_arg(struct double_int64_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_int64_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i64 } @f_ret_double_int64_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_int64_s f_ret_double_int64_s(void) { + return (struct double_int64_s){1.0, 2}; +} + +// LP64-LP64F-LABEL: define dso_local void @f_double_int128bf_s_arg +// LP64-LP64F-SAME: (i128 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_int128bf_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_int128bf_s_arg(struct double_int128bf_s a) {} + +// LP64-LP64F-LABEL: define dso_local i128 @f_ret_double_int128bf_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i64 } @f_ret_double_int128bf_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_int128bf_s f_ret_double_int128bf_s(void) { + return (struct double_int128bf_s){1.0, 2}; +} + +// The zero-width bitfield means the struct can't be passed according to the +// floating point calling convention. + +// LP64-LP64F-LABEL: define dso_local void @f_double_int8_zbf_s +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_double_int8_zbf_s +// LP64D-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_double_int8_zbf_s(struct double_int8_zbf_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_double_int8_zbf_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, i8 } @f_ret_double_int8_zbf_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct double_int8_zbf_s f_ret_double_int8_zbf_s(void) { + return (struct double_int8_zbf_s){1.0, 2}; +} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs +// LP64-LP64F-LP64D-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, + int f, int g, int h, struct double_int8_s i) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_struct_double_int8_insufficient_fprs +// LP64-LP64F-LP64D-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d, + double e, double f, double g, double h, struct double_int8_s i) {} + +// Complex floating-point values or structs containing a single complex +// floating-point value should be passed as if it were an fp+fp struct. + +// LP64-LP64F-LABEL: define dso_local void @f_doublecomplex +// LP64-LP64F-SAME: ([2 x i64] noundef [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublecomplex +// LP64D-SAME: (double noundef [[A_COERCE0:%.*]], double noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublecomplex(double __complex__ a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublecomplex +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublecomplex +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +double __complex__ f_ret_doublecomplex(void) { + return 1.0; +} + +struct doublecomplex_s { double __complex__ c; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublecomplex_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublecomplex_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublecomplex_s_arg(struct doublecomplex_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublecomplex_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublecomplex_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublecomplex_s f_ret_doublecomplex_s(void) { + return (struct doublecomplex_s){1.0}; +} + +// Test single or two-element structs that need flattening. e.g. those +// containing nested structs, doubles in small arrays, zero-length structs etc. + +struct doublearr1_s { double a[1]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr1_s_arg +// LP64-LP64F-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr1_s_arg +// LP64D-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr1_s_arg(struct doublearr1_s a) {} + +// LP64-LP64F-LABEL: define dso_local i64 @f_ret_doublearr1_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local double @f_ret_doublearr1_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr1_s f_ret_doublearr1_s(void) { + return (struct doublearr1_s){{1.0}}; +} + +struct doublearr2_s { double a[2]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr2_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr2_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr2_s_arg(struct doublearr2_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublearr2_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublearr2_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr2_s f_ret_doublearr2_s(void) { + return (struct doublearr2_s){{1.0, 2.0}}; +} + +struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublearr2_tricky1_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky1_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s(void) { + return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublearr2_tricky2_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky2_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s(void) { + return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublearr2_tricky3_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky3_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s(void) { + return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; +} + +struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; }; + +// LP64-LP64F-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg +// LP64-LP64F-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg +// LP64D-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { +// LP64D: entry: +// +void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {} + +// LP64-LP64F-LABEL: define dso_local [2 x i64] @f_ret_doublearr2_tricky4_s +// LP64-LP64F-SAME: () #[[ATTR0]] { +// LP64-LP64F: entry: +// +// LP64D-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky4_s +// LP64D-SAME: () #[[ATTR0]] { +// LP64D: entry: +// +struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s(void) { + return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; +} + +// Test structs that should be passed according to the normal integer calling +// convention. + +struct int_double_int_s { int a; double b; int c; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_int_double_int_s_arg +// LP64-LP64F-LP64D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_int_double_int_s_arg(struct int_double_int_s a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_ret_int_double_int_s +// LP64-LP64F-LP64D-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_INT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct int_double_int_s f_ret_int_double_int_s(void) { + return (struct int_double_int_s){1, 2.0, 3}; +} + +struct char_char_double_s { char a; char b; double c; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_char_char_double_s_arg +// LP64-LP64F-LP64D-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_char_char_double_s_arg(struct char_char_double_s a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local [2 x i64] @f_ret_char_char_double_s +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +struct char_char_double_s f_ret_char_char_double_s(void) { + return (struct char_char_double_s){1, 2, 3.0}; +} + +// Unions are always passed according to the integer calling convention, even +// if they can only contain a double. + +union double_u { double a; }; + +// LP64-LP64F-LP64D-LABEL: define dso_local void @f_double_u_arg +// LP64-LP64F-LP64D-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +void f_double_u_arg(union double_u a) {} + +// LP64-LP64F-LP64D-LABEL: define dso_local i64 @f_ret_double_u +// LP64-LP64F-LP64D-SAME: () #[[ATTR0]] { +// LP64-LP64F-LP64D: entry: +// +union double_u f_ret_double_u(void) { + return (union double_u){1.0}; +} +//// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: +// LP64F: {{.*}} Index: clang/test/CodeGen/RISCV/riscv64-lp64-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-abi.c +++ /dev/null @@ -1,68 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s - -// This file contains test cases that will have different output for lp64 vs -// the other 64-bit ABIs. - -#include -#include - -struct large { - int64_t a, b, c, d; -}; - -typedef unsigned char v32i8 __attribute__((vector_size(32))); - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers. - -// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 -// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], float noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -int f_scalar_stack_1(int32_t a, __int128_t b, float c, long double d, v32i8 e, - uint8_t f, int8_t g, uint8_t h) { - return g + h; -} - -// CHECK-LABEL: define dso_local void @f_scalar_stack_2 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], double noundef [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed in a GPR. - -// CHECK-LABEL: define dso_local void @f_floatcomplex -// CHECK-SAME: (i64 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex(float __complex__ a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_floatcomplex -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -float __complex__ f_ret_floatcomplex(void) { - return 1.0; -} - -struct floatcomplex_s { float __complex__ c; }; - -// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_s_arg(struct floatcomplex_s a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_floatcomplex_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatcomplex_s f_ret_floatcomplex_s(void) { - return (struct floatcomplex_s){1.0}; -} Index: clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-abi.c +++ /dev/null @@ -1,37 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -// This file contains test cases that will have the same output for the lp64 -// and lp64f ABIs. - -#include -#include - -struct large { - int64_t a, b, c, d; -}; - -typedef unsigned char v32i8 __attribute__((vector_size(32))); - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers. - -// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 -// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], double noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -int f_scalar_stack_1(int32_t a, __int128_t b, double c, long double d, v32i8 e, - uint8_t f, int8_t g, uint8_t h) { - return g + h; -} - -// CHECK-LABEL: define dso_local void @f_scalar_stack_2 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], double noundef [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_2(double a, __int128_t b, long double c, v32i8 d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} Index: clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64-lp64f-lp64d-abi.c +++ /dev/null @@ -1,306 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv64 -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ -// RUN: | FileCheck %s -// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -// This file contains test cases that will have the same output for the lp64, -// lp64f, and lp64d ABIs. - -#include -#include - -// CHECK-LABEL: define dso_local void @f_void -// CHECK-SAME: () #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_void(void) {} - -// Scalar arguments and return values smaller than the word size are extended -// according to the sign of their type, up to 32 bits - -// CHECK-LABEL: define dso_local zeroext i1 @f_scalar_0 -// CHECK-SAME: (i1 noundef zeroext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -_Bool f_scalar_0(_Bool x) { return x; } - -// CHECK-LABEL: define dso_local signext i8 @f_scalar_1 -// CHECK-SAME: (i8 noundef signext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int8_t f_scalar_1(int8_t x) { return x; } - -// CHECK-LABEL: define dso_local zeroext i8 @f_scalar_2 -// CHECK-SAME: (i8 noundef zeroext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -uint8_t f_scalar_2(uint8_t x) { return x; } - -// CHECK-LABEL: define dso_local signext i32 @f_scalar_3 -// CHECK-SAME: (i32 noundef signext [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -uint32_t f_scalar_3(int32_t x) { return x; } - -// CHECK-LABEL: define dso_local i64 @f_scalar_4 -// CHECK-SAME: (i64 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int64_t f_scalar_4(int64_t x) { return x; } - -// CHECK-LABEL: define dso_local float @f_fp_scalar_1 -// CHECK-SAME: (float noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -float f_fp_scalar_1(float x) { return x; } - -// CHECK-LABEL: define dso_local double @f_fp_scalar_2 -// CHECK-SAME: (double noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -double f_fp_scalar_2(double x) { return x; } - -// CHECK-LABEL: define dso_local fp128 @f_fp_scalar_3 -// CHECK-SAME: (fp128 noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -long double f_fp_scalar_3(long double x) { return x; } - -// Empty structs or unions are ignored. - -struct empty_s {}; - -// CHECK-LABEL: define dso_local void @f_agg_empty_struct -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct empty_s f_agg_empty_struct(struct empty_s x) { - return x; -} - -union empty_u {}; - -// CHECK-LABEL: define dso_local void @f_agg_empty_union -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union empty_u f_agg_empty_union(union empty_u x) { - return x; -} - -// Aggregates <= 2*xlen may be passed in registers, so will be coerced to -// integer arguments. The rules for return are the same. - -struct tiny { - uint16_t a, b, c, d; -}; - -// CHECK-LABEL: define dso_local void @f_agg_tiny -// CHECK-SAME: (i64 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_tiny(struct tiny x) { - x.a += x.b; - x.c += x.d; -} - -// CHECK-LABEL: define dso_local i64 @f_agg_tiny_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct tiny f_agg_tiny_ret(void) { - return (struct tiny){1, 2, 3, 4}; -} - -typedef uint16_t v4i16 __attribute__((vector_size(8))); -typedef int64_t v1i64 __attribute__((vector_size(8))); - -// CHECK-LABEL: define dso_local void @f_vec_tiny_v4i16 -// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_tiny_v4i16(v4i16 x) { - x[0] = x[1]; - x[2] = x[3]; -} - -// CHECK-LABEL: define dso_local i64 @f_vec_tiny_v4i16_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v4i16 f_vec_tiny_v4i16_ret(void) { - return (v4i16){1, 2, 3, 4}; -} - -// CHECK-LABEL: define dso_local void @f_vec_tiny_v1i64 -// CHECK-SAME: (i64 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_tiny_v1i64(v1i64 x) { - x[0] = 114; -} - -// CHECK-LABEL: define dso_local i64 @f_vec_tiny_v1i64_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v1i64 f_vec_tiny_v1i64_ret(void) { - return (v1i64){1}; -} - -struct small { - int64_t a, *b; -}; - -// CHECK-LABEL: define dso_local void @f_agg_small -// CHECK-SAME: ([2 x i64] [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_small(struct small x) { - x.a += *x.b; - x.b = &x.a; -} - -// CHECK-LABEL: define dso_local [2 x i64] @f_agg_small_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct small f_agg_small_ret(void) { - return (struct small){1, 0}; -} - -typedef uint16_t v8i16 __attribute__((vector_size(16))); -typedef __int128_t v1i128 __attribute__((vector_size(16))); - -// CHECK-LABEL: define dso_local void @f_vec_small_v8i16 -// CHECK-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_small_v8i16(v8i16 x) { - x[0] = x[7]; -} - -// CHECK-LABEL: define dso_local i128 @f_vec_small_v8i16_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v8i16 f_vec_small_v8i16_ret(void) { - return (v8i16){1, 2, 3, 4, 5, 6, 7, 8}; -} - -// CHECK-LABEL: define dso_local void @f_vec_small_v1i128 -// CHECK-SAME: (i128 noundef [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_small_v1i128(v1i128 x) { - x[0] = 114; -} - -// CHECK-LABEL: define dso_local i128 @f_vec_small_v1i128_ret -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -v1i128 f_vec_small_v1i128_ret(void) { - return (v1i128){1}; -} - -// Aggregates of 2*xlen size and 2*xlen alignment should be coerced to a -// single 2*xlen-sized argument, to ensure that alignment can be maintained if -// passed on the stack. - -struct small_aligned { - __int128_t a; -}; - -// CHECK-LABEL: define dso_local void @f_agg_small_aligned -// CHECK-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_small_aligned(struct small_aligned x) { - x.a += x.a; -} - -// CHECK-LABEL: define dso_local i128 @f_agg_small_aligned_ret -// CHECK-SAME: (i128 [[X_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct small_aligned f_agg_small_aligned_ret(struct small_aligned x) { - return (struct small_aligned){10}; -} - -// Aggregates greater > 2*xlen will be passed and returned indirectly -struct large { - int64_t a, b, c, d; -}; - -// CHECK-LABEL: define dso_local void @f_agg_large -// CHECK-SAME: (ptr noundef [[X:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_agg_large(struct large x) { - x.a = x.b + x.c + x.d; -} - -// The address where the struct should be written to will be the first -// argument -// CHECK-LABEL: define dso_local void @f_agg_large_ret -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[I:%.*]], i8 noundef signext [[J:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_agg_large_ret(int32_t i, int8_t j) { - return (struct large){1, 2, 3, 4}; -} - -typedef unsigned char v32i8 __attribute__((vector_size(32))); - -// CHECK-LABEL: define dso_local void @f_vec_large_v32i8 -// CHECK-SAME: (ptr noundef [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_vec_large_v32i8(v32i8 x) { - x[0] = x[7]; -} - -// CHECK-LABEL: define dso_local void @f_vec_large_v32i8_ret -// CHECK-SAME: (ptr noalias sret(<32 x i8>) align 32 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -v32i8 f_vec_large_v32i8_ret(void) { - return (v32i8){1, 2, 3, 4, 5, 6, 7, 8}; -} - -// Scalars passed on the stack should have signext/zeroext attributes, just as -// if they were passed in registers. - -// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_1 -// CHECK-SAME: (i64 [[A_COERCE:%.*]], [2 x i64] [[B_COERCE:%.*]], i128 [[C_COERCE:%.*]], ptr noundef [[D:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]], i8 noundef signext [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int f_scalar_stack_1(struct tiny a, struct small b, struct small_aligned c, - struct large d, uint8_t e, int8_t f, uint8_t g, int8_t h) { - return g + h; -} - -// CHECK-LABEL: define dso_local signext i32 @f_scalar_stack_2 -// CHECK-SAME: (i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], i64 noundef [[C:%.*]], fp128 noundef [[D:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[F:%.*]], i8 noundef signext [[G:%.*]], i8 noundef zeroext [[H:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -int f_scalar_stack_2(int32_t a, __int128_t b, int64_t c, long double d, v32i8 e, - uint8_t f, int8_t g, uint8_t h) { - return g + h; -} - -// Ensure that scalars passed on the stack are still determined correctly in -// the presence of large return values that consume a register due to the need -// to pass a pointer. - -// CHECK-LABEL: define dso_local void @f_scalar_stack_3 -// CHECK-SAME: (ptr noalias sret([[STRUCT_LARGE:%.*]]) align 8 [[AGG_RESULT:%.*]], i32 noundef signext [[A:%.*]], i128 noundef [[B:%.*]], fp128 noundef [[C:%.*]], ptr noundef [[TMP0:%.*]], i8 noundef zeroext [[E:%.*]], i8 noundef signext [[F:%.*]], i8 noundef zeroext [[G:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct large f_scalar_stack_3(uint32_t a, __int128_t b, long double c, v32i8 d, - uint8_t e, int8_t f, uint8_t g) { - return (struct large){a, e, f, g}; -} Index: clang/test/CodeGen/RISCV/riscv64-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64d-abi.c +++ /dev/null @@ -1,417 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -#include - -// Verify that the tracking of used GPRs and FPRs works correctly by checking -// that small integers are sign/zero extended when passed in registers. - -// Doubles are passed in FPRs, so argument 'i' will be passed zero-extended -// because it will be passed in a GPR. - -// CHECK-LABEL: define dso_local void @f_fpr_tracking -// CHECK-SAME: (double noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_fpr_tracking(double a, double b, double c, double d, double e, double f, - double g, double h, uint8_t i) {} - -// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will -// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are -// available the widths are <= XLEN and FLEN, and should be expanded to -// separate arguments in IR. They are passed by the same rules for returns, -// but will be lowered to simple two-element structs if necessary (as LLVM IR -// functions cannot return multiple values). - -// A struct containing just one floating-point real is passed as though it -// were a standalone floating-point real. - -struct double_s { double f; }; - -// CHECK-LABEL: define dso_local void @f_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_s_arg(struct double_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_s f_ret_double_s(void) { - return (struct double_s){1.0}; -} - -// A struct containing a double and any number of zero-width bitfields is -// passed as though it were a standalone floating-point real. - -struct zbf_double_s { int : 0; double f; }; -struct zbf_double_zbf_s { int : 0; double f; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_zbf_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_double_s_arg(struct zbf_double_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_zbf_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_double_s f_ret_zbf_double_s(void) { - return (struct zbf_double_s){1.0}; -} - -// CHECK-LABEL: define dso_local void @f_zbf_double_zbf_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_double_zbf_s_arg(struct zbf_double_zbf_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_zbf_double_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_double_zbf_s f_ret_zbf_double_zbf_s(void) { - return (struct zbf_double_zbf_s){1.0}; -} - -// Check that structs containing two floating point values (FLEN <= width) are -// expanded provided sufficient FPRs are available. - -struct double_double_s { double f; double g; }; -struct double_float_s { double f; float g; }; - -// CHECK-LABEL: define dso_local void @f_double_double_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_double_s_arg(struct double_double_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_double_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_double_s f_ret_double_double_s(void) { - return (struct double_double_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_double_float_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_float_s_arg(struct double_float_s a) {} - -// CHECK-LABEL: define dso_local { double, float } @f_ret_double_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_float_s f_ret_double_float_s(void) { - return (struct double_float_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_double_double_s_arg_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], [2 x i64] [[H_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_double_s_arg_insufficient_fprs(float a, double b, double c, double d, - double e, double f, double g, struct double_double_s h) {} - -// Check that structs containing int+double values are expanded, provided -// sufficient FPRs and GPRs are available. The integer components are neither -// sign or zero-extended. - -struct double_int8_s { double f; int8_t i; }; -struct double_uint8_s { double f; uint8_t i; }; -struct double_int32_s { double f; int32_t i; }; -struct double_int64_s { double f; int64_t i; }; -struct double_int128bf_s { double f; __int128_t i : 64; }; -struct double_int8_zbf_s { double f; int8_t i; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_double_int8_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_s_arg(struct double_int8_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int8_s f_ret_double_int8_s(void) { - return (struct double_int8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_uint8_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_uint8_s_arg(struct double_uint8_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_uint8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_uint8_s f_ret_double_uint8_s(void) { - return (struct double_uint8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int32_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int32_s_arg(struct double_int32_s a) {} - -// CHECK-LABEL: define dso_local { double, i32 } @f_ret_double_int32_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int32_s f_ret_double_int32_s(void) { - return (struct double_int32_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int64_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int64_s_arg(struct double_int64_s a) {} - -// CHECK-LABEL: define dso_local { double, i64 } @f_ret_double_int64_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int64_s f_ret_double_int64_s(void) { - return (struct double_int64_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int128bf_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int128bf_s_arg(struct double_int128bf_s a) {} - -// CHECK-LABEL: define dso_local { double, i64 } @f_ret_double_int128bf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int128bf_s f_ret_double_int128bf_s(void) { - return (struct double_int128bf_s){1.0, 2}; -} - -// The zero-width bitfield means the struct can't be passed according to the -// floating point calling convention. - -// CHECK-LABEL: define dso_local void @f_double_int8_zbf_s -// CHECK-SAME: (double [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_zbf_s(struct double_int8_zbf_s a) {} - -// CHECK-LABEL: define dso_local { double, i8 } @f_ret_double_int8_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct double_int8_zbf_s f_ret_double_int8_zbf_s(void) { - return (struct double_int8_zbf_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_double_int8_s_arg_insufficient_gprs -// CHECK-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, - int f, int g, int h, struct double_int8_s i) {} - -// CHECK-LABEL: define dso_local void @f_struct_double_int8_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], double noundef [[B:%.*]], double noundef [[C:%.*]], double noundef [[D:%.*]], double noundef [[E:%.*]], double noundef [[F:%.*]], double noundef [[G:%.*]], double noundef [[H:%.*]], [2 x i64] [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_struct_double_int8_insufficient_fprs(float a, double b, double c, double d, - double e, double f, double g, double h, struct double_int8_s i) {} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed as if it were an fp+fp struct. - -// CHECK-LABEL: define dso_local void @f_doublecomplex -// CHECK-SAME: (double noundef [[A_COERCE0:%.*]], double noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublecomplex(double __complex__ a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -double __complex__ f_ret_doublecomplex(void) { - return 1.0; -} - -struct doublecomplex_s { double __complex__ c; }; - -// CHECK-LABEL: define dso_local void @f_doublecomplex_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublecomplex_s_arg(struct doublecomplex_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublecomplex_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublecomplex_s f_ret_doublecomplex_s(void) { - return (struct doublecomplex_s){1.0}; -} - -// Test single or two-element structs that need flattening. e.g. those -// containing nested structs, doubles in small arrays, zero-length structs etc. - -struct doublearr1_s { double a[1]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr1_s_arg -// CHECK-SAME: (double [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr1_s_arg(struct doublearr1_s a) {} - -// CHECK-LABEL: define dso_local double @f_ret_doublearr1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr1_s f_ret_doublearr1_s(void) { - return (struct doublearr1_s){{1.0}}; -} - -struct doublearr2_s { double a[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_s_arg(struct doublearr2_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_s f_ret_doublearr2_s(void) { - return (struct doublearr2_s){{1.0, 2.0}}; -} - -struct doublearr2_tricky1_s { struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky1_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky1_s_arg(struct doublearr2_tricky1_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky1_s f_ret_doublearr2_tricky1_s(void) { - return (struct doublearr2_tricky1_s){{{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky2_s { struct {}; struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky2_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky2_s_arg(struct doublearr2_tricky2_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky2_s f_ret_doublearr2_tricky2_s(void) { - return (struct doublearr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky3_s { union {}; struct { double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky3_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky3_s_arg(struct doublearr2_tricky3_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky3_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky3_s f_ret_doublearr2_tricky3_s(void) { - return (struct doublearr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct doublearr2_tricky4_s { union {}; struct { struct {}; double f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_doublearr2_tricky4_s_arg -// CHECK-SAME: (double [[TMP0:%.*]], double [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_doublearr2_tricky4_s_arg(struct doublearr2_tricky4_s a) {} - -// CHECK-LABEL: define dso_local { double, double } @f_ret_doublearr2_tricky4_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct doublearr2_tricky4_s f_ret_doublearr2_tricky4_s(void) { - return (struct doublearr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; -} - -// Test structs that should be passed according to the normal integer calling -// convention. - -struct int_double_int_s { int a; double b; int c; }; - -// CHECK-LABEL: define dso_local void @f_int_double_int_s_arg -// CHECK-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int_double_int_s_arg(struct int_double_int_s a) {} - -// CHECK-LABEL: define dso_local void @f_ret_int_double_int_s -// CHECK-SAME: (ptr noalias sret([[STRUCT_INT_DOUBLE_INT_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -struct int_double_int_s f_ret_int_double_int_s(void) { - return (struct int_double_int_s){1, 2.0, 3}; -} - -struct char_char_double_s { char a; char b; double c; }; - -// CHECK-LABEL: define dso_local void @f_char_char_double_s_arg -// CHECK-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_char_char_double_s_arg(struct char_char_double_s a) {} - -// CHECK-LABEL: define dso_local [2 x i64] @f_ret_char_char_double_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct char_char_double_s f_ret_char_char_double_s(void) { - return (struct char_char_double_s){1, 2, 3.0}; -} - -// Unions are always passed according to the integer calling convention, even -// if they can only contain a double. - -union double_u { double a; }; - -// CHECK-LABEL: define dso_local void @f_double_u_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_double_u_arg(union double_u a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_double_u -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union double_u f_ret_double_u(void) { - return (union double_u){1.0}; -} Index: clang/test/CodeGen/RISCV/riscv64-lp64f-lp64d-abi.c =================================================================== --- clang/test/CodeGen/RISCV/riscv64-lp64f-lp64d-abi.c +++ /dev/null @@ -1,445 +0,0 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --filter "^define |^entry:" --version 2 -// RUN: %clang_cc1 -triple riscv64 -target-feature +f -target-abi lp64f -emit-llvm %s -o - \ -// RUN: | FileCheck %s -// RUN: %clang_cc1 -triple riscv64 -target-feature +d -target-feature +f -target-abi lp64d -emit-llvm %s -o - \ -// RUN: | FileCheck %s - -#include - -// Verify that the tracking of used GPRs and FPRs works correctly by checking -// that small integers are sign/zero extended when passed in registers. - -// Floats are passed in FPRs, so argument 'i' will be passed zero-extended -// because it will be passed in a GPR. - -// CHECK-LABEL: define dso_local void @f_fpr_tracking -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i8 noundef zeroext [[I:%.*]]) #[[ATTR0:[0-9]+]] { -// CHECK: entry: -// -void f_fpr_tracking(float a, float b, float c, float d, float e, float f, - float g, float h, uint8_t i) {} - -// Check that fp, fp+fp, and int+fp structs are lowered correctly. These will -// be passed in FPR, FPR+FPR, or GPR+FPR regs if sufficient registers are -// available the widths are <= XLEN and FLEN, and should be expanded to -// separate arguments in IR. They are passed by the same rules for returns, -// but will be lowered to simple two-element structs if necessary (as LLVM IR -// functions cannot return multiple values). - -// A struct containing just one floating-point real is passed as though it -// were a standalone floating-point real. - -struct float_s { float f; }; - -// CHECK-LABEL: define dso_local void @f_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_s_arg(struct float_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_s f_ret_float_s(void) { - return (struct float_s){1.0}; -} - -// A struct containing a float and any number of zero-width bitfields is -// passed as though it were a standalone floating-point real. - -struct zbf_float_s { int : 0; float f; }; -struct zbf_float_zbf_s { int : 0; float f; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_zbf_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_float_s_arg(struct zbf_float_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_zbf_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_float_s f_ret_zbf_float_s(void) { - return (struct zbf_float_s){1.0}; -} - -// CHECK-LABEL: define dso_local void @f_zbf_float_zbf_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_zbf_float_zbf_s_arg(struct zbf_float_zbf_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_zbf_float_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct zbf_float_zbf_s f_ret_zbf_float_zbf_s(void) { - return (struct zbf_float_zbf_s){1.0}; -} - -// Check that structs containing two float values (FLEN <= width) are expanded -// provided sufficient FPRs are available. - -struct float_float_s { float f; float g; }; - -// CHECK-LABEL: define dso_local void @f_float_float_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_float_s_arg(struct float_float_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_float_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_float_s f_ret_float_float_s(void) { - return (struct float_float_s){1.0, 2.0}; -} - -// CHECK-LABEL: define dso_local void @f_float_float_s_arg_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], i64 [[H_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_float_s_arg_insufficient_fprs(float a, float b, float c, float d, - float e, float f, float g, struct float_float_s h) {} - -// Check that structs containing int+float values are expanded, provided -// sufficient FPRs and GPRs are available. The integer components are neither -// sign or zero-extended. - -struct float_int8_s { float f; int8_t i; }; -struct float_uint8_s { float f; uint8_t i; }; -struct float_int32_s { float f; int32_t i; }; -struct float_int64_s { float f; int64_t i; }; -struct float_int128bf_s { float f; __int128_t i : 64; }; -struct float_int8_zbf_s { float f; int8_t i; int : 0; }; - -// CHECK-LABEL: define dso_local void @f_float_int8_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_s_arg(struct float_int8_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int8_s f_ret_float_int8_s(void) { - return (struct float_int8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_uint8_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_uint8_s_arg(struct float_uint8_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_uint8_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_uint8_s f_ret_float_uint8_s(void) { - return (struct float_uint8_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int32_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int32_s_arg(struct float_int32_s a) {} - -// CHECK-LABEL: define dso_local { float, i32 } @f_ret_float_int32_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int32_s f_ret_float_int32_s(void) { - return (struct float_int32_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int64_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int64_s_arg(struct float_int64_s a) {} - -// CHECK-LABEL: define dso_local { float, i64 } @f_ret_float_int64_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int64_s f_ret_float_int64_s(void) { - return (struct float_int64_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int128bf_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], i64 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int128bf_s_arg(struct float_int128bf_s a) {} - -// CHECK-LABEL: define dso_local <{ float, i64 }> @f_ret_float_int128bf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int128bf_s f_ret_float_int128bf_s(void) { - return (struct float_int128bf_s){1.0, 2}; -} - -// The zero-width bitfield means the struct can't be passed according to the -// floating point calling convention. - -// CHECK-LABEL: define dso_local void @f_float_int8_zbf_s -// CHECK-SAME: (float [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_zbf_s(struct float_int8_zbf_s a) {} - -// CHECK-LABEL: define dso_local { float, i8 } @f_ret_float_int8_zbf_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct float_int8_zbf_s f_ret_float_int8_zbf_s(void) { - return (struct float_int8_zbf_s){1.0, 2}; -} - -// CHECK-LABEL: define dso_local void @f_float_int8_s_arg_insufficient_gprs -// CHECK-SAME: (i32 noundef signext [[A:%.*]], i32 noundef signext [[B:%.*]], i32 noundef signext [[C:%.*]], i32 noundef signext [[D:%.*]], i32 noundef signext [[E:%.*]], i32 noundef signext [[F:%.*]], i32 noundef signext [[G:%.*]], i32 noundef signext [[H:%.*]], i64 [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, - int f, int g, int h, struct float_int8_s i) {} - -// CHECK-LABEL: define dso_local void @f_struct_float_int8_insufficient_fprs -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B:%.*]], float noundef [[C:%.*]], float noundef [[D:%.*]], float noundef [[E:%.*]], float noundef [[F:%.*]], float noundef [[G:%.*]], float noundef [[H:%.*]], i64 [[I_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_struct_float_int8_insufficient_fprs(float a, float b, float c, float d, - float e, float f, float g, float h, struct float_int8_s i) {} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed as if it were an fp+fp struct. - -// CHECK-LABEL: define dso_local void @f_floatcomplex -// CHECK-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex(float __complex__ a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -float __complex__ f_ret_floatcomplex(void) { - return 1.0; -} - -struct floatcomplex_s { float __complex__ c; }; - -// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_s_arg(struct floatcomplex_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatcomplex_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatcomplex_s f_ret_floatcomplex_s(void) { - return (struct floatcomplex_s){1.0}; -} - -// Complex floating-point values or structs containing a single complex -// floating-point value should be passed in GPRs if no two FPRs is available. - -// CHECK-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs1 -// CHECK-SAME: (float noundef [[A_COERCE0:%.*]], float noundef [[A_COERCE1:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_insufficient_fprs1(float __complex__ a, float __complex__ b, - float __complex__ c, float __complex__ d, - float __complex__ e) {} - - -// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs1 -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], float [[TMP6:%.*]], float [[TMP7:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_s_arg_insufficient_fprs1(struct floatcomplex_s a, - struct floatcomplex_s b, - struct floatcomplex_s c, - struct floatcomplex_s d, - struct floatcomplex_s e) {} - -// CHECK-LABEL: define dso_local void @f_floatcomplex_insufficient_fprs2 -// CHECK-SAME: (float noundef [[A:%.*]], float noundef [[B_COERCE0:%.*]], float noundef [[B_COERCE1:%.*]], float noundef [[C_COERCE0:%.*]], float noundef [[C_COERCE1:%.*]], float noundef [[D_COERCE0:%.*]], float noundef [[D_COERCE1:%.*]], i64 noundef [[E_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_insufficient_fprs2(float a, - float __complex__ b, float __complex__ c, - float __complex__ d, float __complex__ e) {} - - -// CHECK-LABEL: define dso_local void @f_floatcomplex_s_arg_insufficient_fprs2 -// CHECK-SAME: (float noundef [[A:%.*]], float [[TMP0:%.*]], float [[TMP1:%.*]], float [[TMP2:%.*]], float [[TMP3:%.*]], float [[TMP4:%.*]], float [[TMP5:%.*]], i64 [[E_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatcomplex_s_arg_insufficient_fprs2(float a, - struct floatcomplex_s b, - struct floatcomplex_s c, - struct floatcomplex_s d, - struct floatcomplex_s e) {} - -// Test single or two-element structs that need flattening. e.g. those -// containing nested structs, floats in small arrays, zero-length structs etc. - -struct floatarr1_s { float a[1]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr1_s_arg -// CHECK-SAME: (float [[TMP0:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr1_s_arg(struct floatarr1_s a) {} - -// CHECK-LABEL: define dso_local float @f_ret_floatarr1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr1_s f_ret_floatarr1_s(void) { - return (struct floatarr1_s){{1.0}}; -} - -struct floatarr2_s { float a[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_s_arg(struct floatarr2_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_s f_ret_floatarr2_s(void) { - return (struct floatarr2_s){{1.0, 2.0}}; -} - -struct floatarr2_tricky1_s { struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky1_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky1_s_arg(struct floatarr2_tricky1_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky1_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky1_s f_ret_floatarr2_tricky1_s(void) { - return (struct floatarr2_tricky1_s){{{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky2_s { struct {}; struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky2_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky2_s_arg(struct floatarr2_tricky2_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky2_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky2_s f_ret_floatarr2_tricky2_s(void) { - return (struct floatarr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky3_s { union {}; struct { float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky3_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky3_s_arg(struct floatarr2_tricky3_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky3_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky3_s f_ret_floatarr2_tricky3_s(void) { - return (struct floatarr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; -} - -struct floatarr2_tricky4_s { union {}; struct { struct {}; float f[1]; } g[2]; }; - -// CHECK-LABEL: define dso_local void @f_floatarr2_tricky4_s_arg -// CHECK-SAME: (float [[TMP0:%.*]], float [[TMP1:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_floatarr2_tricky4_s_arg(struct floatarr2_tricky4_s a) {} - -// CHECK-LABEL: define dso_local { float, float } @f_ret_floatarr2_tricky4_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct floatarr2_tricky4_s f_ret_floatarr2_tricky4_s(void) { - return (struct floatarr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; -} - -// Test structs that should be passed according to the normal integer calling -// convention. - -struct int_float_int_s { int a; float b; int c; }; - -// CHECK-LABEL: define dso_local void @f_int_float_int_s_arg -// CHECK-SAME: ([2 x i64] [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_int_float_int_s_arg(struct int_float_int_s a) {} - -// CHECK-LABEL: define dso_local [2 x i64] @f_ret_int_float_int_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct int_float_int_s f_ret_int_float_int_s(void) { - return (struct int_float_int_s){1, 2.0, 3}; -} - -struct char_char_float_s { char a; char b; float c; }; - -// CHECK-LABEL: define dso_local void @f_char_char_float_s_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_char_char_float_s_arg(struct char_char_float_s a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_char_char_float_s -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -struct char_char_float_s f_ret_char_char_float_s(void) { - return (struct char_char_float_s){1, 2, 3.0}; -} - -// Unions are always passed according to the integer calling convention, even -// if they can only contain a float. - -union float_u { float a; }; - -// CHECK-LABEL: define dso_local void @f_float_u_arg -// CHECK-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { -// CHECK: entry: -// -void f_float_u_arg(union float_u a) {} - -// CHECK-LABEL: define dso_local i64 @f_ret_float_u -// CHECK-SAME: () #[[ATTR0]] { -// CHECK: entry: -// -union float_u f_ret_float_u(void) { - return (union float_u){1.0}; -}