| // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --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 <stddef.h> |
| #include <stdint.h> |
| |
| // 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; } |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local half @f_fp_scalar_4 |
| // ILP32-ILP32F-ILP32D-SAME: (half noundef [[X:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| _Float16 f_fp_scalar_4(_Float16 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:%.*]], half noundef [[I:%.*]]) #[[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, _Float16 i) { |
| 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}; |
| } |
| |
| // 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). |
| |
| struct float16_s { _Float16 f; }; |
| |
| // A struct containing just one floating-point real is passed as though it |
| // were a standalone floating-point real. |
| |
| // ILP32-LABEL: define dso_local void @f_float16_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_s_arg(struct float16_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local half @f_ret_float16_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_s f_ret_float16_s(void) { |
| return (struct float16_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_float16_s { int : 0; _Float16 f; }; |
| struct zbf_float16_zbf_s { int : 0; _Float16 f; int : 0; }; |
| |
| // ILP32-LABEL: define dso_local void @f_zbf_float16_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_zbf_float16_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_zbf_float16_s_arg(struct zbf_float16_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_zbf_float16_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local half @f_ret_zbf_float16_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct zbf_float16_s f_ret_zbf_float16_s(void) { |
| return (struct zbf_float16_s){1.0}; |
| } |
| |
| // ILP32-LABEL: define dso_local void @f_zbf_float16_zbf_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_zbf_float16_zbf_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_zbf_float16_zbf_s_arg(struct zbf_float16_zbf_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_zbf_float16_zbf_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local half @f_ret_zbf_float16_zbf_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct zbf_float16_zbf_s f_ret_zbf_float16_zbf_s(void) { |
| return (struct zbf_float16_zbf_s){1.0}; |
| } |
| |
| // Check that structs containing two floating point values (FLEN <= width) are |
| // expanded provided sufficient FPRs are available. |
| |
| struct double_float16_s { double f; _Float16 g; }; |
| |
| // ILP32-ILP32F-LABEL: define dso_local void @f_double_float16_s_arg |
| // ILP32-ILP32F-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F: entry: |
| // |
| // ILP32D-LABEL: define dso_local void @f_double_float16_s_arg |
| // ILP32D-SAME: (double [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32D: entry: |
| // |
| void f_double_float16_s_arg(struct double_float16_s a) {} |
| |
| // ILP32-ILP32F-LABEL: define dso_local void @f_ret_double_float16_s |
| // ILP32-ILP32F-SAME: (ptr noalias sret([[STRUCT_DOUBLE_FLOAT16_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F: entry: |
| // |
| // ILP32D-LABEL: define dso_local { double, half } @f_ret_double_float16_s |
| // ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32D: entry: |
| // |
| struct double_float16_s f_ret_double_float16_s(void) { |
| return (struct double_float16_s){1.0, 2.0}; |
| } |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_double_float16_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_float16_s_arg_insufficient_fprs(float a, double b, double c, double d, |
| double e, double f, double g, struct double_float16_s h) {} |
| |
| // Check that structs containing int+_Float16 values are expanded, provided |
| // sufficient FPRs and GPRs are available. The integer components are neither |
| // sign or zero-extended. |
| |
| struct float16_int8_s { _Float16 f; int8_t i; }; |
| struct float16_uint8_s { _Float16 f; uint8_t i; }; |
| struct float16_int32_s { _Float16 f; int32_t i; }; |
| struct float16_int64_s { _Float16 f; int64_t i; }; |
| struct float16_int64bf_s { _Float16 f; int64_t i : 32; }; |
| struct float16_int8_zbf_s { _Float16 f; int8_t i; int : 0; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16_int8_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_int8_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_int8_s_arg(struct float16_int8_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16_int8_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, i8 } @f_ret_float16_int8_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_int8_s f_ret_float16_int8_s(void) { |
| return (struct float16_int8_s){1.0, 2}; |
| } |
| |
| // ILP32-LABEL: define dso_local void @f_float16_uint8_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_uint8_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_uint8_s_arg(struct float16_uint8_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16_uint8_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, i8 } @f_ret_float16_uint8_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_uint8_s f_ret_float16_uint8_s(void) { |
| return (struct float16_uint8_s){1.0, 2}; |
| } |
| |
| // ILP32-LABEL: define dso_local void @f_float16_int32_s_arg |
| // ILP32-SAME: ([2 x i32] [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_int32_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_int32_s_arg(struct float16_int32_s a) {} |
| |
| // ILP32-LABEL: define dso_local [2 x i32] @f_ret_float16_int32_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, i32 } @f_ret_float16_int32_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_int32_s f_ret_float16_int32_s(void) { |
| return (struct float16_int32_s){1.0, 2}; |
| } |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float16_int64_s_arg |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_float16_int64_s_arg(struct float16_int64_s a) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_float16_int64_s |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_FLOAT16_INT64_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| struct float16_int64_s f_ret_float16_int64_s(void) { |
| return (struct float16_int64_s){1.0, 2}; |
| } |
| |
| // ILP32-LABEL: define dso_local void @f_float16_int64bf_s_arg |
| // ILP32-SAME: (i64 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_int64bf_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], i32 [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_int64bf_s_arg(struct float16_int64bf_s a) {} |
| |
| // ILP32-LABEL: define dso_local i64 @f_ret_float16_int64bf_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local <{ half, i32 }> @f_ret_float16_int64bf_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_int64bf_s f_ret_float16_int64bf_s(void) { |
| return (struct float16_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_float16_int8_zbf_s |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16_int8_zbf_s |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], i8 [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16_int8_zbf_s(struct float16_int8_zbf_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16_int8_zbf_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, i8 } @f_ret_float16_int8_zbf_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16_int8_zbf_s f_ret_float16_int8_zbf_s(void) { |
| return (struct float16_int8_zbf_s){1.0, 2}; |
| } |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float16_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:%.*]], i32 [[I_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_float16_int8_s_arg_insufficient_gprs(int a, int b, int c, int d, int e, |
| int f, int g, int h, struct float16_int8_s i) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_struct_float16_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:%.*]], i32 [[I_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_struct_float16_int8_insufficient_fprs(float a, double b, double c, double d, |
| double e, double f, double g, double h, struct float16_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_float16complex |
| // ILP32-SAME: (i32 noundef [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16complex |
| // ILP32F-ILP32D-SAME: (half noundef [[A_COERCE0:%.*]], half noundef [[A_COERCE1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16complex(_Float16 __complex__ a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16complex |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16complex |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| _Float16 __complex__ f_ret_float16complex(void) { |
| return 1.0; |
| } |
| |
| struct float16complex_s { _Float16 __complex__ c; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16complex_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16complex_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16complex_s_arg(struct float16complex_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16complex_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16complex_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16complex_s f_ret_float16complex_s(void) { |
| return (struct float16complex_s){1.0}; |
| } |
| |
| // Test single or two-element structs that need flattening. e.g. those |
| // containing nested structs, _Float16 in small arrays, zero-length structs etc. |
| |
| struct float16arr1_s { _Float16 a[1]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr1_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr1_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr1_s_arg(struct float16arr1_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr1_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local half @f_ret_float16arr1_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr1_s f_ret_float16arr1_s(void) { |
| return (struct float16arr1_s){{1.0}}; |
| } |
| |
| struct float16arr2_s { _Float16 a[2]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr2_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr2_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr2_s_arg(struct float16arr2_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr2_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16arr2_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr2_s f_ret_float16arr2_s(void) { |
| return (struct float16arr2_s){{1.0, 2.0}}; |
| } |
| |
| struct float16arr2_tricky1_s { struct { _Float16 f[1]; } g[2]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr2_tricky1_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr2_tricky1_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr2_tricky1_s_arg(struct float16arr2_tricky1_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr2_tricky1_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16arr2_tricky1_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr2_tricky1_s f_ret_float16arr2_tricky1_s(void) { |
| return (struct float16arr2_tricky1_s){{{{1.0}}, {{2.0}}}}; |
| } |
| |
| struct float16arr2_tricky2_s { struct {}; struct { _Float16 f[1]; } g[2]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr2_tricky2_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr2_tricky2_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr2_tricky2_s_arg(struct float16arr2_tricky2_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr2_tricky2_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16arr2_tricky2_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr2_tricky2_s f_ret_float16arr2_tricky2_s(void) { |
| return (struct float16arr2_tricky2_s){{}, {{{1.0}}, {{2.0}}}}; |
| } |
| |
| struct float16arr2_tricky3_s { union {}; struct { _Float16 f[1]; } g[2]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr2_tricky3_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr2_tricky3_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr2_tricky3_s_arg(struct float16arr2_tricky3_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr2_tricky3_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16arr2_tricky3_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr2_tricky3_s f_ret_float16arr2_tricky3_s(void) { |
| return (struct float16arr2_tricky3_s){{}, {{{1.0}}, {{2.0}}}}; |
| } |
| |
| struct float16arr2_tricky4_s { union {}; struct { struct {}; _Float16 f[1]; } g[2]; }; |
| |
| // ILP32-LABEL: define dso_local void @f_float16arr2_tricky4_s_arg |
| // ILP32-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local void @f_float16arr2_tricky4_s_arg |
| // ILP32F-ILP32D-SAME: (half [[TMP0:%.*]], half [[TMP1:%.*]]) #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| void f_float16arr2_tricky4_s_arg(struct float16arr2_tricky4_s a) {} |
| |
| // ILP32-LABEL: define dso_local i32 @f_ret_float16arr2_tricky4_s |
| // ILP32-SAME: () #[[ATTR0]] { |
| // ILP32: entry: |
| // |
| // ILP32F-ILP32D-LABEL: define dso_local { half, half } @f_ret_float16arr2_tricky4_s |
| // ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32F-ILP32D: entry: |
| // |
| struct float16arr2_tricky4_s f_ret_float16arr2_tricky4_s(void) { |
| return (struct float16arr2_tricky4_s){{}, {{{}, {1.0}}, {{}, {2.0}}}}; |
| } |
| |
| // Test structs that should be passed according to the normal integer calling |
| // convention. |
| |
| struct int_float16_int_s { int a; _Float16 b; int c; }; |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int_float16_int_s_arg |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_int_float16_int_s_arg(struct int_float16_int_s a) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int_float16_int_s |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT_FLOAT16_INT_S:%.*]]) align 4 [[AGG_RESULT:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| struct int_float16_int_s f_ret_int_float16_int_s(void) { |
| return (struct int_float16_int_s){1, 2.0, 3}; |
| } |
| |
| struct int64_float16_s { int64_t a; _Float16 b; }; |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_int64_float16_s_arg |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noundef [[A:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_int64_float16_s_arg(struct int64_float16_s a) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_ret_int64_float16_s |
| // ILP32-ILP32F-ILP32D-SAME: (ptr noalias sret([[STRUCT_INT64_FLOAT16_S:%.*]]) align 8 [[AGG_RESULT:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| struct int64_float16_s f_ret_int64_float16_s(void) { |
| return (struct int64_float16_s){1, 2.0}; |
| } |
| |
| struct char_char_float16_s { char a; char b; _Float16 c; }; |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_char_char_float16_s_arg |
| // ILP32-ILP32F-ILP32D-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_char_char_float16_s_arg(struct char_char_float16_s a) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_ret_char_char_float16_s |
| // ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| struct char_char_float16_s f_ret_char_char_float16_s(void) { |
| return (struct char_char_float16_s){1, 2, 3.0}; |
| } |
| |
| // Unions are always passed according to the integer calling convention, even |
| // if they can only contain a double. |
| |
| union float16_u { _Float16 a; }; |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local void @f_float16_u_arg |
| // ILP32-ILP32F-ILP32D-SAME: (i32 [[A_COERCE:%.*]]) #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| void f_float16_u_arg(union float16_u a) {} |
| |
| // ILP32-ILP32F-ILP32D-LABEL: define dso_local i32 @f_ret_float16_u |
| // ILP32-ILP32F-ILP32D-SAME: () #[[ATTR0]] { |
| // ILP32-ILP32F-ILP32D: entry: |
| // |
| union float16_u f_ret_float16_u(void) { |
| return (union float16_u){1.0}; |
| } |
| |
| //// NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line: |
| // ILP32F: {{.*}} |