diff --git a/libguile/lightening/lightening.h b/libguile/lightening/lightening.h index 4749723bf..020c47975 100644 --- a/libguile/lightening/lightening.h +++ b/libguile/lightening/lightening.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2019 Free Software Foundation, Inc. + * Copyright (C) 2012-2020 Free Software Foundation, Inc. * * This file is part of GNU lightning. * @@ -661,4 +661,13 @@ FOR_EACH_INSTRUCTION(DECLARE_INSTRUCTION) # define jit_truncr_f(j,u,v) jit_truncr_f_l(j,u,v) #endif +void jit_begin_data(jit_state_t *); +void jit_end_data(jit_state_t *); +void jit_emit_u8(jit_state_t *, uint8_t); +void jit_emit_u16(jit_state_t *, uint16_t); +void jit_emit_u32(jit_state_t *, uint32_t); +void jit_emit_u64(jit_state_t *, uint64_t); +void jit_emit_ptr(jit_state_t *, void *); +jit_reloc_t jit_emit_addr(jit_state_t *); + #endif /* _jit_h */ diff --git a/libguile/lightening/lightening/aarch64.c b/libguile/lightening/lightening/aarch64.c index 2b3ed4d60..e67365f23 100644 --- a/libguile/lightening/lightening/aarch64.c +++ b/libguile/lightening/lightening/aarch64.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2019 Free Software Foundation, Inc. + * Copyright (C) 2013-2020 Free Software Foundation, Inc. * * This file is part of GNU lightning. * @@ -132,7 +132,7 @@ struct veneer static void patch_veneer(uint32_t *loc, jit_pointer_t addr) { - struct veneer *v = (struct veneer*) v; + struct veneer *v = (struct veneer*) loc; v->addr = (uint64_t) addr; } diff --git a/libguile/lightening/lightening/lightening.c b/libguile/lightening/lightening/lightening.c index 6948a81ee..775b2b644 100644 --- a/libguile/lightening/lightening/lightening.c +++ b/libguile/lightening/lightening/lightening.c @@ -74,6 +74,7 @@ struct jit_state uint8_t temp_gpr_saved; uint8_t temp_fpr_saved; uint8_t overflow; + uint8_t emitting_data; int frame_size; // Used to know when to align stack. #ifdef JIT_NEEDS_LITERAL_POOL struct jit_literal_pool *pool; @@ -171,7 +172,8 @@ jit_pointer_t jit_address(jit_state_t *_jit) { ASSERT (_jit->start); - return jit_address_to_function_pointer (_jit->pc.uc); + jit_pointer_t ret = _jit->pc.uc; + return _jit->emitting_data ? ret : jit_address_to_function_pointer (ret); } void @@ -183,6 +185,7 @@ jit_begin(jit_state_t *_jit, uint8_t* buf, size_t length) _jit->limit = buf + length; _jit->overflow = 0; _jit->frame_size = 0; + _jit->emitting_data = 0; #if JIT_NEEDS_LITERAL_POOL ASSERT(_jit->pool->size == 0); _jit->pool->deadline = length; @@ -203,6 +206,7 @@ jit_reset(jit_state_t *_jit) _jit->pc.uc = _jit->start = _jit->limit = NULL; _jit->overflow = 0; _jit->frame_size = 0; + _jit->emitting_data = 0; #ifdef JIT_NEEDS_LITERAL_POOL reset_literal_pool(_jit, _jit->pool); #endif @@ -228,9 +232,10 @@ jit_end(jit_state_t *_jit, size_t *length) uint8_t *start = _jit->start; uint8_t *end = _jit->pc.uc; - ASSERT (start); - ASSERT (start <= end); - ASSERT (end <= _jit->limit); + ASSERT(start); + ASSERT(start <= end); + ASSERT(end <= _jit->limit); + ASSERT(!_jit->emitting_data); jit_flush (start, end); @@ -343,9 +348,9 @@ static inline void emit_u64(jit_state_t *_jit, uint64_t u64) { } static inline jit_reloc_t -jit_reloc (jit_state_t *_jit, enum jit_reloc_kind kind, - uint8_t inst_start_offset, uint8_t *loc, uint8_t *pc_base, - uint8_t rsh) +jit_reloc(jit_state_t *_jit, enum jit_reloc_kind kind, + uint8_t inst_start_offset, uint8_t *loc, uint8_t *pc_base, + uint8_t rsh) { jit_reloc_t ret; @@ -362,6 +367,17 @@ jit_reloc (jit_state_t *_jit, enum jit_reloc_kind kind, return ret; } +static inline jit_reloc_t +emit_abs_reloc (jit_state_t *_jit, uint8_t inst_start) +{ + uint8_t *loc = _jit->pc.uc; + if (sizeof(intptr_t) == 4) + emit_u32 (_jit, 0); + else + emit_u64 (_jit, 0); + return jit_reloc(_jit, JIT_RELOC_ABSOLUTE, inst_start, loc, _jit->pc.uc, 0); +} + void jit_patch_here(jit_state_t *_jit, jit_reloc_t reloc) { @@ -467,6 +483,66 @@ jit_patch_there(jit_state_t* _jit, jit_reloc_t reloc, jit_pointer_t addr) jit_try_shorten (_jit, reloc, addr); } +void +jit_begin_data(jit_state_t *j) +{ +#ifdef JIT_NEEDS_LITERAL_POOL + if (j->pool->size) + emit_literal_pool(j, NO_GUARD_NEEDED); + ASSERT(j->overflow || j->pool->size == 0); +#endif + + ASSERT(!j->emitting_data); + j->emitting_data = 1; +} + +void +jit_end_data(jit_state_t *j) +{ +#ifdef JIT_NEEDS_LITERAL_POOL + ASSERT(j->overflow || j->pool->size == 0); +#endif + + ASSERT(j->emitting_data); + j->emitting_data = 0; +} + +void +jit_emit_u8(jit_state_t *j, uint8_t u8) +{ + ASSERT(j->emitting_data); + emit_u8(j, u8); +} + +void +jit_emit_u16(jit_state_t *j, uint16_t u16) +{ + ASSERT(j->emitting_data); + emit_u16(j, u16); +} + +void +jit_emit_u32(jit_state_t *j, uint32_t u32) +{ + ASSERT(j->emitting_data); + emit_u32(j, u32); +} + +void +jit_emit_u64(jit_state_t *j, uint64_t u64) +{ + ASSERT(j->emitting_data); + emit_u64(j, u64); +} + +jit_reloc_t +jit_emit_addr(jit_state_t *j) +{ + ASSERT(j->emitting_data); + uint8_t inst_start = 0; + return emit_abs_reloc(j, inst_start); +} + #if defined(__i386__) || defined(__x86_64__) # include "x86.c" #elif defined(__mips__) diff --git a/libguile/lightening/lightening/x86.c b/libguile/lightening/lightening/x86.c index bdd26e14f..5d75eb012 100644 --- a/libguile/lightening/lightening/x86.c +++ b/libguile/lightening/lightening/x86.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2019 Free Software Foundation, Inc. + * Copyright (C) 2012-2020 Free Software Foundation, Inc. * * This file is part of GNU lightning. * @@ -76,17 +76,6 @@ emit_rel32_reloc (jit_state_t *_jit, uint8_t inst_start) return jit_reloc(_jit, JIT_RELOC_REL32, inst_start, loc, _jit->pc.uc, 0); } -static inline jit_reloc_t -emit_abs_reloc (jit_state_t *_jit, uint8_t inst_start) -{ - uint8_t *loc = _jit->pc.uc; - if (sizeof(intptr_t) == 4) - emit_u32 (_jit, 0); - else - emit_u64 (_jit, 0); - return jit_reloc(_jit, JIT_RELOC_ABSOLUTE, inst_start, loc, _jit->pc.uc, 0); -} - #include "x86-cpu.c" #include "x86-sse.c" @@ -375,6 +364,9 @@ jit_try_shorten(jit_state_t *_jit, jit_reloc_t reloc, jit_pointer_t addr) uint8_t *start = loc - reloc.inst_start_offset; jit_imm_t i0 = (jit_imm_t)addr; + if (loc == start) + return; + switch (reloc.kind) { case JIT_RELOC_ABSOLUTE: { diff --git a/libguile/lightening/tests/jmp_table.c b/libguile/lightening/tests/jmp_table.c new file mode 100644 index 000000000..6d86b659d --- /dev/null +++ b/libguile/lightening/tests/jmp_table.c @@ -0,0 +1,61 @@ +#include "test.h" + +#define NTARGETS ((size_t) 4) + +static void +run_test(jit_state_t *j, uint8_t *arena_base, size_t arena_size) +{ + jit_begin(j, arena_base, arena_size); + size_t align = jit_enter_jit_abi(j, 0, 0, 0); + jit_load_args_1(j, jit_operand_gpr (JIT_OPERAND_ABI_POINTER, JIT_R0)); + + jit_reloc_t default_target = jit_bgei_u(j, JIT_R0, NTARGETS); + + // FIXME: need ldxr with word stride, then can eliminate lshi. + jit_lshi(j, JIT_R0, JIT_R0, sizeof(intptr_t) == 4 ? 2 : 3); + jit_reloc_t table = jit_mov_addr(j, JIT_R1); + jit_ldxr(j, JIT_R1, JIT_R1, JIT_R0); + jit_jmpr(j, JIT_R1); + + jit_begin_data (j); + jit_align(j, sizeof(intptr_t)); + jit_patch_here(j, table); + jit_reloc_t targets[NTARGETS]; + jit_reloc_t tails[NTARGETS]; + for (size_t i = 0; i < NTARGETS; i++) { + targets[i] = jit_emit_addr(j); + } + jit_end_data (j); + + for (size_t i = 0; i < NTARGETS; i++) { + jit_patch_here(j, targets[i]); + jit_movi(j, JIT_R0, i * i); + tails[i] = jit_jmp(j); + } + + jit_patch_here(j, default_target); + jit_movi(j, JIT_R0, 42); + for (int i = 0; i < NTARGETS; i++) { + jit_patch_here(j, tails[i]); + } + jit_leave_jit_abi(j, 0, 0, align); + jit_retr(j, JIT_R0); + + jit_word_t (*f)(jit_word_t) = jit_end(j, NULL); + + for (int i = -2; i < ((int) NTARGETS) + 2; i++) { + if (i < 0) { + ASSERT(f(i) == 42); + } else if (i < NTARGETS) { + ASSERT(f(i) == i * i); + } else { + ASSERT(f(i) == 42); + } + } +} + +int +main (int argc, char *argv[]) +{ + return main_helper(argc, argv, run_test); +} diff --git a/libguile/lightening/tests/jmpi.c b/libguile/lightening/tests/jmpi.c index 2f9213fec..e73ace055 100644 --- a/libguile/lightening/tests/jmpi.c +++ b/libguile/lightening/tests/jmpi.c @@ -1,21 +1,41 @@ #include "test.h" -static int tail(void) { return 42; } +void *tail; + +static void *target; static void run_test(jit_state_t *j, uint8_t *arena_base, size_t arena_size) { jit_begin(j, arena_base, arena_size); - - jit_jmpi(j, tail); - + jit_enter_jit_abi(j, 0, 0, 0); + jit_movi(j, JIT_R0, 42); + jit_jmpi(j, target); + // Unreachable. + jit_breakpoint(j); int (*f)(void) = jit_end(j, NULL); - ASSERT(f() == 42); } +// Make the tail-call target via a separate main_helper because probably the new +// arena will be allocated farther away, forcing nonlocal jumps. +static void +make_target(jit_state_t *j, uint8_t *arena_base, size_t arena_size) +{ + jit_begin(j, arena_base, arena_size); + size_t align = jit_enter_jit_abi(j, 0, 0, 0); + // Tail call target assumes tail caller called enter_jit_abi with compatible + // parameters. + target = jit_address(j); + jit_leave_jit_abi(j, 0, 0, align); + jit_retr(j, JIT_R0); + jit_end(j, NULL); + + main_helper(0, NULL, run_test); +} + int main (int argc, char *argv[]) { - return main_helper(argc, argv, run_test); + return main_helper(argc, argv, make_target); }