mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-04-29 19:30:36 +02:00
328 lines
7.6 KiB
C
328 lines
7.6 KiB
C
/*
|
|
* 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)
|
|
{
|
|
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;
|
|
}
|
|
*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;
|
|
}
|
|
|