From 7c20ba77672df6a5d6f8ffc6aab00b5693087f92 Mon Sep 17 00:00:00 2001 From: Ekaitz Zarraga Date: Mon, 18 Nov 2024 21:08:41 +0100 Subject: [PATCH] riscv: float/double call convention implementation RISC-V uses a0-a7 registers for argument passing. Float/double arguments use f0-f7 first and continue in a0-a7 if needed. Once registers are consumed, stack is used. This commit changes how lightening passes arguments in order to allow this behavior. --- lightening/lightening.c | 27 ++++++++++++++++++++++++++- lightening/riscv-fpu.c | 25 +++++++++++++++++++++++++ lightening/riscv.c | 13 +++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lightening/lightening.c b/lightening/lightening.c index c66b3a132..f26a467ff 100644 --- a/lightening/lightening.c +++ b/lightening/lightening.c @@ -807,6 +807,14 @@ abi_mem_to_gpr(jit_state_t *_jit, enum jit_operand_abi abi, case JIT_OPERAND_ABI_INT16: jit_ldxi_s(_jit, dst, base, offset); break; + case JIT_OPERAND_ABI_FLOAT: + { + jit_fpr_t tmp = get_temp_fpr(_jit); + jit_ldxi_f(_jit, tmp, base, offset); + jit_movr_i_f(_jit, dst, tmp); + unget_temp_fpr(_jit); + break; + } #if __WORDSIZE == 32 case JIT_OPERAND_ABI_UINT32: case JIT_OPERAND_ABI_POINTER: @@ -823,6 +831,14 @@ abi_mem_to_gpr(jit_state_t *_jit, enum jit_operand_abi abi, case JIT_OPERAND_ABI_INT64: jit_ldxi_l(_jit, dst, base, offset); break; + case JIT_OPERAND_ABI_DOUBLE: + { + jit_fpr_t tmp = get_temp_fpr(_jit); + jit_ldxi_d(_jit, tmp, base, offset); + jit_movr_l_d(_jit, dst, tmp); + unget_temp_fpr(_jit); + break; + } #endif default: abort(); @@ -887,7 +903,8 @@ enum move_kind { MOVE_KIND_ENUM(IMM, MEM), MOVE_KIND_ENUM(GPR, MEM), MOVE_KIND_ENUM(FPR, MEM), - MOVE_KIND_ENUM(MEM, MEM) + MOVE_KIND_ENUM(MEM, MEM), + MOVE_KIND_ENUM(FPR, GPR) }; #undef MOVE_KIND_ENUM @@ -901,6 +918,14 @@ move_operand(jit_state_t *_jit, jit_operand_t dst, jit_operand_t src) case MOVE_GPR_TO_GPR: return jit_movr(_jit, dst.loc.gpr.gpr, src.loc.gpr.gpr); + case MOVE_FPR_TO_GPR: +#if __WORDSIZE > 32 + if (src.abi == JIT_OPERAND_ABI_DOUBLE) + return jit_movr_l_d(_jit, dst.loc.gpr.gpr, src.loc.fpr); + else +#endif + return jit_movr_i_f(_jit, dst.loc.gpr.gpr, src.loc.fpr); + case MOVE_MEM_TO_GPR: return abi_mem_to_gpr(_jit, src.abi, dst.loc.gpr.gpr, src.loc.mem.base, src.loc.mem.offset); diff --git a/lightening/riscv-fpu.c b/lightening/riscv-fpu.c index 315ed8d14..b4e7546c7 100644 --- a/lightening/riscv-fpu.c +++ b/lightening/riscv-fpu.c @@ -103,6 +103,10 @@ static void absr_d(jit_state_t *_jit, int32_t r0, int32_t r1); // Transfer operations static void movr_f(jit_state_t *_jit, int32_t r0, int32_t r1); static void movr_d(jit_state_t *_jit, int32_t r0, int32_t r1); +static void movr_i_f(jit_state_t *_jit, int32_t r0, int32_t r1); +static void movr_l_d(jit_state_t *_jit, int32_t r0, int32_t r1); +static void movr_f_i(jit_state_t *_jit, int32_t r0, int32_t r1); +static void movr_d_l(jit_state_t *_jit, int32_t r0, int32_t r1); // Argument management static void retr_f(jit_state_t *_jit, int32_t u); @@ -398,6 +402,27 @@ movr_d(jit_state_t *_jit, int32_t r0, int32_t r1) if (r0 != r1) em_wp(_jit, _FMV_D(r0, r1)); } +static void +movr_i_f(jit_state_t *_jit, int32_t r0, int32_t r1) +{ + em_wp(_jit, _FMV_X_W(r0, r1)); +} +static void +movr_f_i(jit_state_t *_jit, int32_t r0, int32_t r1) +{ + em_wp(_jit, _FMV_W_X(r0, r1)); +} +static void +movr_l_d(jit_state_t *_jit, int32_t r0, int32_t r1) +{ + em_wp(_jit, _FMV_X_D(r0, r1)); +} +static void +movr_d_l(jit_state_t *_jit, int32_t r0, int32_t r1) +{ + em_wp(_jit, _FMV_D_X(r0, r1)); +} + static void truncr_f_i(jit_state_t *_jit, int32_t r0, int32_t r1) { diff --git a/lightening/riscv.c b/lightening/riscv.c index 3f0adce46..d3e4efaa3 100644 --- a/lightening/riscv.c +++ b/lightening/riscv.c @@ -111,6 +111,16 @@ reset_abi_arg_iterator(struct abi_arg_iterator *iter, size_t argc, static void next_abi_arg(struct abi_arg_iterator *iter, jit_operand_t *arg) { + // RISC-V Calling convention: + // https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf + // + // The RISC-V calling convention passes arguments in registers when possible. + // Up to eight integer registers, a0–a7, and up to eight floating-point + // registers, fa0–fa7, are used for this purpose. + // + // If argument i < 8 is a floating-point type, it is passed in floating-point + // register fai; otherwise, it is passed in integer register ai. + ASSERT(iter->arg_idx < iter->argc); enum jit_operand_abi abi = iter->args[iter->arg_idx].abi; iter->arg_idx++; @@ -121,6 +131,9 @@ next_abi_arg(struct abi_arg_iterator *iter, jit_operand_t *arg) if (is_fpr_arg(abi) && iter->fpr_idx < abi_fpr_arg_count) { *arg = jit_operand_fpr (abi, abi_fpr_args[iter->fpr_idx++]); return; + } else if (is_fpr_arg(abi) && iter->gpr_idx < abi_gpr_arg_count) { + *arg = jit_operand_gpr (abi, abi_gpr_args[iter->gpr_idx++]); + return; } *arg = jit_operand_mem (abi, JIT_SP, iter->stack_size); #if __WORDSIZE == 32