1
Fork 0
mirror of https://git.savannah.gnu.org/git/guile.git synced 2025-04-29 19:30:36 +02:00
guile/lightening/riscv.c
Ekaitz Zarraga 7c20ba7767 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.
2025-01-29 14:53:04 +01:00

341 lines
8.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2021-2024 Free Software Foundation, Inc.
*
* This file is part of GNU lightning.
*
* GNU lightning is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* GNU lightning is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* Authors:
* Ekaitz Zarraga <ekaitz@elenq.tech>
*/
#include "riscv-cpu.c"
#include "riscv-fpu.c"
static const jit_gpr_t abi_gpr_args[] = {
_A0, _A1, _A2, _A3, _A4, _A5, _A6, _A7
};
static const jit_fpr_t abi_fpr_args[] = {
_FA0, _FA1, _FA2, _FA3, _FA4, _FA5, _FA6, _FA7
};
static const int abi_gpr_arg_count = sizeof(abi_gpr_args) / sizeof(abi_gpr_args[0]);
static const int abi_fpr_arg_count = sizeof(abi_fpr_args) / sizeof(abi_fpr_args[0]);
struct abi_arg_iterator
{
const jit_operand_t *args;
size_t argc;
size_t arg_idx;
size_t gpr_idx;
size_t fpr_idx;
uint32_t vfp_used_registers;
size_t stack_size;
size_t stack_padding;
};
static size_t page_size;
jit_bool_t
jit_get_cpu(void)
{
page_size = sysconf(_SC_PAGE_SIZE);
// FIXME check version, extensions, hardware fp support
//
// List of macro definitions for riscv support:
// -------------------------------------------
// __riscv: defined for any RISC-V target. Older versions of the GCC
// toolchain defined __riscv__.
//
// __riscv_xlen: 32 for RV32 and 64 for RV64.
//
// __riscv_float_abi_soft, __riscv_float_abi_single,
// __riscv_float_abi_double: one of these three will be defined, depending on
// target ABI.
//
// __riscv_cmodel_medlow, __riscv_cmodel_medany: one of these two will be
// defined, depending on the target code model.
//
// __riscv_mul: defined when targeting the 'M' ISA extension.
//
// __riscv_muldiv: defined when targeting the 'M' ISA extension and -mno-div
// has not been used.
//
// __riscv_div: defined when targeting the 'M' ISA extension and -mno-div has
// not been used.
//
// __riscv_atomic: defined when targeting the 'A' ISA extension.
//
// __riscv_flen: 32 when targeting the 'F' ISA extension (but not 'D') and 64
// when targeting 'FD'.
//
// __riscv_fdiv: defined when targeting the 'F' or 'D' ISA extensions and
// -mno-fdiv has not been used.
//
// __riscv_fsqrt: defined when targeting the 'F' or 'D' ISA extensions and
// -mno-fdiv has not been used.
//
// __riscv_compressed: defined when targeting the 'C' ISA extension.
return 1;
}
jit_bool_t
jit_init(jit_state_t *_jit)
{
return 1;
}
static size_t
jit_initial_frame_size (void)
{
return 0;
}
static void
reset_abi_arg_iterator(struct abi_arg_iterator *iter, size_t argc,
const jit_operand_t *args)
{
memset(iter, 0, sizeof *iter);
iter->argc = argc;
iter->args = args;
}
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, a0a7, and up to eight floating-point
// registers, fa0fa7, 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++;
if (is_gpr_arg(abi) && iter->gpr_idx < abi_gpr_arg_count) {
*arg = jit_operand_gpr (abi, abi_gpr_args[iter->gpr_idx++]);
return;
}
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
iter->stack_size += 4 + (abi == JIT_OPERAND_ABI_DOUBLE ? 4 : 0);
#elif __WORDSIZE == 64
iter->stack_size += 8;
#endif
}
static void
jit_flush(void *fptr, void *tptr)
{
jit_word_t f = (jit_word_t)fptr & -page_size;
jit_word_t t = (((jit_word_t)tptr) + page_size - 1) & -page_size;
__clear_cache((void *)f, (void *)t);
}
static inline size_t
jit_stack_alignment(void)
{
return 16;
// NOTE: See: https://github.com/riscv/riscv-gcc/issues/61
}
static void
jit_try_shorten(jit_state_t *_jit, jit_reloc_t reloc, jit_pointer_t addr)
{
}
static void*
bless_function_pointer(void *ptr)
{
return ptr;
}
static jit_gpr_t
get_callr_temp (jit_state_t * _jit)
{
return _RA;
}
/*
* Veneers
*/
struct veneer{
instr_t auipc;
instr_t load; // `ld` in RV64 and `lw` in RV32
instr_t jalr;
#if __WORDSIZE == 64
uint32_t padding;
uint64_t address;
#elif __WORDSIZE == 32
uint32_t address;
#endif
};
static void
emit_veneer(jit_state_t *_jit, jit_pointer_t target)
{
// We need to generate something like this:
// ----------------------------------------------
// 32 bits: | 64 bits:
// auipc t0, 0 | auipc t0, 0
// ld t0, 12(t0) | ld t0, 16(t0)
// jalr zero, 0(t0) | jalr zero, 0(t0)
// ADDRESS_LITERAL | .byte 0x00, 0x00, 0x00, 0x00 (padding)
// | ADDRESS_LITERAL
//
jit_gpr_t t0 = get_temp_gpr(_jit);
emit_u32(_jit, _AUIPC(jit_gpr_regno(t0), 0));
#if __WORDSIZE == 64
emit_u32(_jit, _LD(jit_gpr_regno(t0), jit_gpr_regno(t0), 16));
#elif __WORDSIZE == 32
emit_u32(_jit, _LW(jit_gpr_regno(t0), jit_gpr_regno(t0), 12));
#endif
emit_u32(_jit, _JALR(jit_gpr_regno(_ZERO), jit_gpr_regno(t0), 0));
#if __WORDSIZE == 64
emit_u32(_jit, 0); // Padding
emit_u64(_jit, (uint64_t) target);
#elif __WORDSIZE == 32
emit_u32(_jit, (uint32_t) target);
#endif
unget_temp_gpr(_jit);
}
static void
patch_veneer(uint32_t *loc, jit_pointer_t addr)
{
struct veneer *v = (struct veneer*) loc;
#if __WORDSIZE == 64
v->address = (uint64_t) addr;
#elif __WORDSIZE == 32
v->address = (uint32_t) addr;
#endif
}
/*
* Conditional jumps
*/
static void
patch_jcc_offset(uint32_t *loc, ptrdiff_t v)
{
instr_t *i = (instr_t *) loc;
i->w = patch_cc_jump(i->w, v);
}
static void
patch_veneer_jcc_offset(uint32_t *loc, ptrdiff_t offset){
patch_jcc_offset(loc, offset);
}
static int32_t
read_jcc_offset(uint32_t *loc)
{
instr_t i;
i.w = *loc;
int32_t offset = i.B.imm12 << 31;
offset >>= 20;
offset |= (i.B.imm11 << 11);
offset |= (i.B.imm10_5 << 5);
offset |= (i.B.imm4_1 << 1);
return offset;
}
static int
offset_in_jcc_range(ptrdiff_t offset, int flags)
{
if(offset & 1)
return 0;
else
return simm12_p(offset >> 1);
}
/*
* Unconditional jumps
*/
static int32_t read_jmp_offset(uint32_t *loc)
{
instr_t i;
i.w = *loc;
int32_t offset = i.J.imm20 << 31;
offset >>= 12;
offset |= (i.J.imm19_12 << 12);
offset |= (i.J.imm11 << 11);
offset |= (i.J.imm10_1 << 1);
return offset;
}
static int
offset_in_jmp_range(ptrdiff_t offset, int flags)
{
if(offset & 1)
return 0;
else
return simm20_p(offset >> 1);
}
static void
patch_jmp_offset(uint32_t *loc, ptrdiff_t v)
{
instr_t *i = (instr_t *) loc;
i->w = patch_jump(i->w, v);
}
static void
patch_veneer_jmp_offset(uint32_t *loc, ptrdiff_t offset)
{
patch_jmp_offset(loc, offset);
}
/*
* Jumps around the veneer
*/
static void
patch_jmp_without_veneer(jit_state_t *_jit, uint32_t *loc)
{
patch_jmp_offset(loc, _jit->pc.uw - (uintptr_t)loc);
}
static uint32_t*
jmp_without_veneer(jit_state_t *_jit)
{
uint32_t *loc = _jit->pc.ui;
emit_u32(_jit, _JAL(jit_gpr_regno(_ZERO), 0));
return loc;
}
/*
* Load from pool offset
*/
static void
patch_load_from_pool_offset(uint32_t *loc, int32_t v)
{
load_from_pool_t *i = (load_from_pool_t *) loc;
i->l = patch_load_from_pool(i->l, v);
}
static int32_t
read_load_from_pool_offset(uint32_t *loc)
{
load_from_pool_t *i = (load_from_pool_t*) loc;
return i->inst.auipc.U.imm31_12 + i->inst.load.I.imm11_0;
}