From: LibretroAdmin <105389611+LibretroAdmin@users.noreply.github.com> Date: Sat, 4 Jun 2022 21:48:57 +0000 (+0100) Subject: Merge pull request #661 from pcercuei/update-lightrec-20220604 X-Git-Tag: r24l~463 X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=473f5cc63f9942866e01c7c84d151e8157cdb6ee;hp=74a0bb9f83cb116827118571351a2f4880a0fb7e;p=pcsx_rearmed.git Merge pull request #661 from pcercuei/update-lightrec-20220604 Update lightrec 20220604 --- diff --git a/Makefile b/Makefile index 0860c722..3aa06b7d 100644 --- a/Makefile +++ b/Makefile @@ -96,7 +96,7 @@ CFLAGS += -Ideps/lightning/include -Ideps/lightrec -Iinclude/lightning -Iinclude deps/lightning/lib/%.o: CFLAGS += -DHAVE_MMAP ifeq ($(LIGHTREC_CUSTOM_MAP),1) LDLIBS += -lrt -OBJS += libpcsxcore/lightrec/mem.o +OBJS += libpcsxcore/lightrec/mem.o deps/lightrec/tlsf/tlsf.o endif OBJS += libpcsxcore/lightrec/plugin.o OBJS += deps/lightning/lib/jit_disasm.o \ diff --git a/deps/lightrec/.gitrepo b/deps/lightrec/.gitrepo index c9d423a7..809f13b2 100644 --- a/deps/lightrec/.gitrepo +++ b/deps/lightrec/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/pcercuei/lightrec.git branch = master - commit = 49ef275a66aad8540ab73b09b0dd2128ebe4d6dc - parent = a0467ff492a25521867fcfb7d66b9c617017151a + commit = a8fe7568cef718517b230a13fe48891faa60f04b + parent = 2f609094ad9ec14212fb730897ccb63f2f44bc40 method = merge cmdver = 0.4.3 diff --git a/deps/lightrec/CMakeLists.txt b/deps/lightrec/CMakeLists.txt index 7b285185..aa8440b6 100644 --- a/deps/lightrec/CMakeLists.txt +++ b/deps/lightrec/CMakeLists.txt @@ -91,15 +91,6 @@ if (CMAKE_C_COMPILER_ID STREQUAL "Clang") target_compile_options(${PROJECT_NAME} PRIVATE -Wno-initializer-overrides) endif() -option(ENABLE_TINYMM "Enable optional libtinymm dependency" OFF) -if (ENABLE_TINYMM) - find_library(TINYMM_LIBRARIES tinymm REQUIRED) - find_path(TINYMM_INCLUDE_DIR tinymm.h REQUIRED) - - include_directories(${TINYMM_INCLUDE_DIR}) - target_link_libraries(${PROJECT_NAME} PRIVATE ${TINYMM_LIBRARIES}) -endif (ENABLE_TINYMM) - if (ENABLE_THREADED_COMPILER) find_library(PTHREAD_LIBRARIES pthread REQUIRED) find_path(PTHREAD_INCLUDE_DIR pthread.h REQUIRED) @@ -114,10 +105,6 @@ if (ENABLE_CODE_BUFFER) target_include_directories(${PROJECT_NAME} PRIVATE tlsf) endif (ENABLE_CODE_BUFFER) -if (ENABLE_CODE_BUFFER AND ENABLE_THREADED_COMPILER) - message(SEND_ERROR "External code buffer cannot be used along with the threaded compiler") -endif (ENABLE_CODE_BUFFER AND ENABLE_THREADED_COMPILER) - find_library(LIBLIGHTNING lightning REQUIRED) find_path(LIBLIGHTNING_INCLUDE_DIR lightning.h REQUIRED) diff --git a/deps/lightrec/blockcache.c b/deps/lightrec/blockcache.c index 2182f298..70c5aebe 100644 --- a/deps/lightrec/blockcache.c +++ b/deps/lightrec/blockcache.c @@ -102,18 +102,55 @@ void lightrec_unregister_block(struct blockcache *cache, struct block *block) pr_err("Block at PC 0x%x is not in cache\n", block->pc); } -void lightrec_free_block_cache(struct blockcache *cache) +static bool lightrec_block_is_old(const struct lightrec_state *state, + const struct block *block) +{ + u32 diff = state->current_cycle - block->precompile_date; + + return diff > (1 << 27); /* About 4 seconds */ +} + +static void lightrec_free_blocks(struct blockcache *cache, + const struct block *except, bool all) { + struct lightrec_state *state = cache->state; struct block *block, *next; + bool outdated = all; unsigned int i; for (i = 0; i < LUT_SIZE; i++) { for (block = cache->lut[i]; block; block = next) { next = block->next; - lightrec_free_block(cache->state, block); + + if (except && block == except) + continue; + + if (!all) { + outdated = lightrec_block_is_old(state, block) || + lightrec_block_is_outdated(state, block); + } + + if (outdated) { + pr_debug("Freeing outdated block at PC 0x%08x\n", block->pc); + remove_from_code_lut(cache, block); + lightrec_unregister_block(cache, block); + lightrec_free_block(state, block); + } } } +} + +void lightrec_remove_outdated_blocks(struct blockcache *cache, + const struct block *except) +{ + pr_info("Running out of code space. Cleaning block cache...\n"); + lightrec_free_blocks(cache, except, false); +} + +void lightrec_free_block_cache(struct blockcache *cache) +{ + lightrec_free_blocks(cache, NULL, true); lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache); } diff --git a/deps/lightrec/blockcache.h b/deps/lightrec/blockcache.h index 3b782f47..2e55ff65 100644 --- a/deps/lightrec/blockcache.h +++ b/deps/lightrec/blockcache.h @@ -24,4 +24,7 @@ void lightrec_free_block_cache(struct blockcache *cache); u32 lightrec_calculate_block_hash(const struct block *block); _Bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block); +void lightrec_remove_outdated_blocks(struct blockcache *cache, + const struct block *except); + #endif /* __BLOCKCACHE_H__ */ diff --git a/deps/lightrec/disassembler.c b/deps/lightrec/disassembler.c index cb332c67..9b2dbd53 100644 --- a/deps/lightrec/disassembler.c +++ b/deps/lightrec/disassembler.c @@ -213,6 +213,9 @@ static int print_op_special(union code c, char *buf, size_t len, lightrec_reg_name(c.r.rt), lightrec_reg_name(c.r.rs)); case OP_SPECIAL_JR: + *flags_ptr = opcode_branch_flags; + *nb_flags = ARRAY_SIZE(opcode_branch_flags); + fallthrough; case OP_SPECIAL_MTHI: case OP_SPECIAL_MTLO: return snprintf(buf, len, "%s%s", @@ -300,6 +303,8 @@ static int print_op(union code c, u32 pc, char *buf, size_t len, pc + 4 + ((s16)c.i.imm << 2)); case OP_J: case OP_JAL: + *flags_ptr = opcode_branch_flags; + *nb_flags = ARRAY_SIZE(opcode_branch_flags); return snprintf(buf, len, "%s0x%x", std_opcodes[c.i.op], (pc & 0xf0000000) | (c.j.imm << 2)); diff --git a/deps/lightrec/lightrec-config.h.cmakein b/deps/lightrec/lightrec-config.h.cmakein index f5524e9d..11886653 100644 --- a/deps/lightrec/lightrec-config.h.cmakein +++ b/deps/lightrec/lightrec-config.h.cmakein @@ -9,7 +9,6 @@ #cmakedefine01 ENABLE_THREADED_COMPILER #cmakedefine01 ENABLE_FIRST_PASS #cmakedefine01 ENABLE_DISASSEMBLER -#cmakedefine01 ENABLE_TINYMM #cmakedefine01 ENABLE_CODE_BUFFER #cmakedefine01 HAS_DEFAULT_ELM diff --git a/deps/lightrec/lightrec-private.h b/deps/lightrec/lightrec-private.h index 87565a63..fe89e66b 100644 --- a/deps/lightrec/lightrec-private.h +++ b/deps/lightrec/lightrec-private.h @@ -43,6 +43,8 @@ #define SET_DEFAULT_ELM(table, value) [0] = NULL #endif +#define fallthrough do {} while (0) /* fall-through */ + /* Flags for (struct block *)->flags */ #define BLOCK_NEVER_COMPILE BIT(0) #define BLOCK_SHOULD_RECOMPILE BIT(1) @@ -67,7 +69,6 @@ struct blockcache; struct recompiler; struct regcache; struct opcode; -struct tinymm; struct reaper; struct block { @@ -78,6 +79,7 @@ struct block { struct block *next; u32 pc; u32 hash; + u32 precompile_date; unsigned int code_size; u16 nb_ops; u8 flags; @@ -131,7 +133,6 @@ struct lightrec_state { struct block *dispatcher, *c_wrapper_block; void *c_wrappers[C_WRAPPERS_COUNT]; void *wrappers_eps[C_WRAPPERS_COUNT]; - struct tinymm *tinymm; struct blockcache *block_cache; struct recompiler *rec; struct lightrec_cstate *cstate; @@ -207,7 +208,7 @@ static inline void ** lut_address(struct lightrec_state *state, u32 offset) static inline void * lut_read(struct lightrec_state *state, u32 offset) { - void **lut_entry = lut_address(state, lut_offset(offset)); + void **lut_entry = lut_address(state, offset); if (lut_is_32bit(state)) return (void *)(uintptr_t) *(u32 *) lut_entry; diff --git a/deps/lightrec/lightrec.c b/deps/lightrec/lightrec.c index d172a30a..ba734ad2 100644 --- a/deps/lightrec/lightrec.c +++ b/deps/lightrec/lightrec.c @@ -27,9 +27,6 @@ #include #include #include -#if ENABLE_TINYMM -#include -#endif #define GENMASK(h, l) \ (((uintptr_t)-1 << (l)) & ((uintptr_t)-1 >> (__WORDSIZE - 1 - (h)))) @@ -328,7 +325,8 @@ static void lightrec_rw_helper(struct lightrec_state *state, case OP_LW: if (op.i.rt) state->regs.gpr[op.i.rt] = ret; - default: /* fall-through */ + fallthrough; + default: break; } } @@ -402,7 +400,8 @@ static u32 lightrec_mfc2(struct lightrec_state *state, u8 reg) clamp_s32(gteir3 >> 7, 0, 0x1f) << 10; case 15: reg = 14; - default: /* fall-through */ + fallthrough; + default: return state->regs.cp2d[reg]; } } @@ -503,7 +502,8 @@ static void lightrec_mtc2(struct lightrec_state *state, u8 reg, u32 data) return; case 30: state->regs.cp2d[31] = count_leading_bits((s32) data); - default: /* fall-through */ + fallthrough; + default: state->regs.cp2d[reg] = data; break; } @@ -523,7 +523,8 @@ static void lightrec_ctc2(struct lightrec_state *state, u8 reg, u32 data) break; case 31: data = (data & 0x7ffff000) | !!(data & 0x7f87e000) << 31; - default: /* fall-through */ + fallthrough; + default: break; } @@ -623,9 +624,10 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc) struct block *block; bool should_recompile; void *func; + int err; for (;;) { - func = lut_read(state, pc); + func = lut_read(state, lut_offset(pc)); if (func && func != state->get_next_block) break; @@ -647,10 +649,15 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc) lightrec_unregister(MEM_FOR_CODE, block->code_size); - if (ENABLE_THREADED_COMPILER) + if (ENABLE_THREADED_COMPILER) { lightrec_recompiler_add(state->rec, block); - else - lightrec_compile_block(state->cstate, block); + } else { + err = lightrec_compile_block(state->cstate, block); + if (err) { + state->exit_flags = LIGHTREC_EXIT_NOMEM; + return NULL; + } + } } if (ENABLE_THREADED_COMPILER && likely(!should_recompile)) @@ -672,7 +679,11 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc) pc = lightrec_emulate_block(state, block, pc); /* Then compile it using the profiled data */ - lightrec_compile_block(state->cstate, block); + err = lightrec_compile_block(state->cstate, block); + if (err) { + state->exit_flags = LIGHTREC_EXIT_NOMEM; + return NULL; + } } else { lightrec_recompiler_add(state->rec, block); } @@ -696,7 +707,49 @@ static s32 c_function_wrapper(struct lightrec_state *state, s32 cycles_delta, return state->target_cycle - state->current_cycle; } +static void * lightrec_alloc_code(struct lightrec_state *state, size_t size) +{ + void *code; + + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_lock(state); + + code = tlsf_malloc(state->tlsf, size); + + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_unlock(state); + + return code; +} + +static void lightrec_realloc_code(struct lightrec_state *state, + void *ptr, size_t size) +{ + /* NOTE: 'size' MUST be smaller than the size specified during + * the allocation. */ + + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_lock(state); + + tlsf_realloc(state->tlsf, ptr, size); + + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_unlock(state); +} + +static void lightrec_free_code(struct lightrec_state *state, void *ptr) +{ + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_lock(state); + + tlsf_free(state->tlsf, ptr); + + if (ENABLE_THREADED_COMPILER) + lightrec_code_alloc_unlock(state); +} + static void * lightrec_emit_code(struct lightrec_state *state, + const struct block *block, jit_state_t *_jit, unsigned int *size) { bool has_code_buffer = ENABLE_CODE_BUFFER && state->tlsf; @@ -710,9 +763,28 @@ static void * lightrec_emit_code(struct lightrec_state *state, if (has_code_buffer) { jit_get_code(&code_size); - code = tlsf_malloc(state->tlsf, (size_t) code_size); - if (!code) - return NULL; + code = lightrec_alloc_code(state, (size_t) code_size); + + if (!code) { + if (ENABLE_THREADED_COMPILER) { + /* If we're using the threaded compiler, return + * an allocation error here. The threaded + * compiler will then empty its job queue and + * request a code flush using the reaper. */ + return NULL; + } + + /* Remove outdated blocks, and try again */ + lightrec_remove_outdated_blocks(state->block_cache, block); + + pr_debug("Re-try to alloc %zu bytes...\n", code_size); + + code = lightrec_alloc_code(state, code_size); + if (!code) { + pr_err("Could not alloc even after removing old blocks!\n"); + return NULL; + } + } jit_set_code(code, code_size); } @@ -723,7 +795,7 @@ static void * lightrec_emit_code(struct lightrec_state *state, lightrec_register(MEM_FOR_CODE, new_code_size); if (has_code_buffer) { - tlsf_realloc(state->tlsf, code, new_code_size); + lightrec_realloc_code(state, code, (size_t) new_code_size); pr_debug("Creating code block at address 0x%" PRIxPTR ", " "code size: %" PRIuPTR " new: %" PRIuPTR "\n", @@ -819,7 +891,7 @@ static struct block * generate_wrapper(struct lightrec_state *state) block->flags = 0; block->nb_ops = 0; - block->function = lightrec_emit_code(state, _jit, + block->function = lightrec_emit_code(state, block, _jit, &block->code_size); if (!block->function) goto err_free_block; @@ -1002,7 +1074,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state) block->flags = 0; block->nb_ops = 0; - block->function = lightrec_emit_code(state, _jit, + block->function = lightrec_emit_code(state, block, _jit, &block->code_size); if (!block->function) goto err_free_block; @@ -1124,6 +1196,7 @@ static struct block * lightrec_precompile_block(struct lightrec_state *state, block->next = NULL; block->flags = 0; block->code_size = 0; + block->precompile_date = state->current_cycle; #if ENABLE_THREADED_COMPILER block->op_list_freed = (atomic_flag)ATOMIC_FLAG_INIT; #endif @@ -1189,7 +1262,8 @@ static bool lightrec_block_is_fully_tagged(const struct block *block) case OP_SWC2: if (!LIGHTREC_FLAGS_GET_IO_MODE(op->flags)) return false; - default: /* fall-through */ + fallthrough; + default: continue; } } @@ -1215,7 +1289,7 @@ static void lightrec_free_function(struct lightrec_state *state, void *fn) { if (ENABLE_CODE_BUFFER && state->tlsf) { pr_debug("Freeing code block at 0x%" PRIxPTR "\n", (uintptr_t) fn); - tlsf_free(state->tlsf, fn); + lightrec_free_code(state, fn); } } @@ -1235,7 +1309,7 @@ int lightrec_compile_block(struct lightrec_cstate *cstate, jit_state_t *_jit, *oldjit; jit_node_t *start_of_block; bool skip_next = false; - void *old_fn; + void *old_fn, *new_fn; unsigned int i, j; u32 offset; @@ -1326,12 +1400,16 @@ int lightrec_compile_block(struct lightrec_cstate *cstate, jit_ret(); jit_epilog(); - block->function = lightrec_emit_code(state, _jit, - &block->code_size); - if (!block->function) { - pr_err("Unable to compile block!\n"); + new_fn = lightrec_emit_code(state, block, _jit, &block->code_size); + if (!new_fn) { + if (!ENABLE_THREADED_COMPILER) + pr_err("Unable to compile block!\n"); + block->_jit = oldjit; + _jit_destroy_state(_jit); + return -ENOMEM; } + block->function = new_fn; block->flags &= ~BLOCK_SHOULD_RECOMPILE; /* Add compiled function to the LUT */ @@ -1504,8 +1582,10 @@ void lightrec_free_block(struct lightrec_state *state, struct block *block) lightrec_free_opcode_list(state, block); if (block->_jit) _jit_destroy_state(block->_jit); - lightrec_free_function(state, block->function); - lightrec_unregister(MEM_FOR_CODE, block->code_size); + if (block->function) { + lightrec_free_function(state, block->function); + lightrec_unregister(MEM_FOR_CODE, block->code_size); + } lightrec_free(state, MEM_FOR_IR, sizeof(*block), block); } @@ -1539,7 +1619,7 @@ struct lightrec_state * lightrec_init(char *argv0, size_t nb, const struct lightrec_ops *ops) { - const struct lightrec_mem_map *codebuf_map; + const struct lightrec_mem_map *codebuf_map = &map[PSX_MAP_CODE_BUFFER]; struct lightrec_state *state; uintptr_t addr; void *tlsf = NULL; @@ -1552,9 +1632,8 @@ struct lightrec_state * lightrec_init(char *argv0, return NULL; } - if (ENABLE_CODE_BUFFER && nb > PSX_MAP_CODE_BUFFER) { - codebuf_map = &map[PSX_MAP_CODE_BUFFER]; - + if (ENABLE_CODE_BUFFER && nb > PSX_MAP_CODE_BUFFER + && codebuf_map->address) { tlsf = tlsf_create_with_pool(codebuf_map->address, codebuf_map->length); if (!tlsf) { @@ -1584,15 +1663,9 @@ struct lightrec_state * lightrec_init(char *argv0, state->tlsf = tlsf; state->with_32bit_lut = with_32bit_lut; -#if ENABLE_TINYMM - state->tinymm = tinymm_init(malloc, free, 4096); - if (!state->tinymm) - goto err_free_state; -#endif - state->block_cache = lightrec_blockcache_init(state); if (!state->block_cache) - goto err_free_tinymm; + goto err_free_state; if (ENABLE_THREADED_COMPILER) { state->rec = lightrec_recompiler_init(state); @@ -1668,11 +1741,7 @@ err_free_recompiler: lightrec_free_cstate(state->cstate); err_free_block_cache: lightrec_free_block_cache(state->block_cache); -err_free_tinymm: -#if ENABLE_TINYMM - tinymm_shutdown(state->tinymm); err_free_state: -#endif lightrec_unregister(MEM_FOR_LIGHTREC, sizeof(*state) + lut_elm_size(state) * CODE_LUT_SIZE); free(state); @@ -1703,9 +1772,6 @@ void lightrec_destroy(struct lightrec_state *state) if (ENABLE_CODE_BUFFER && state->tlsf) tlsf_destroy(state->tlsf); -#if ENABLE_TINYMM - tinymm_shutdown(state->tinymm); -#endif lightrec_unregister(MEM_FOR_LIGHTREC, sizeof(*state) + lut_elm_size(state) * CODE_LUT_SIZE); free(state); @@ -1714,17 +1780,23 @@ void lightrec_destroy(struct lightrec_state *state) void lightrec_invalidate(struct lightrec_state *state, u32 addr, u32 len) { u32 kaddr = kunseg(addr & ~0x3); - const struct lightrec_mem_map *map = lightrec_get_map(state, NULL, kaddr); - - if (map) { - if (map != &state->maps[PSX_MAP_KERNEL_USER_RAM]) - return; + enum psx_map idx = lightrec_get_map_idx(state, kaddr); + switch (idx) { + case PSX_MAP_MIRROR1: + case PSX_MAP_MIRROR2: + case PSX_MAP_MIRROR3: /* Handle mirrors */ - kaddr &= (state->maps[PSX_MAP_KERNEL_USER_RAM].length - 1); - - lightrec_invalidate_map(state, map, kaddr, len); + kaddr &= RAM_SIZE - 1; + fallthrough; + case PSX_MAP_KERNEL_USER_RAM: + break; + default: + return; } + + memset(lut_address(state, lut_offset(kaddr)), 0, + ((len + 3) / 4) * lut_elm_size(state)); } void lightrec_invalidate_all(struct lightrec_state *state) diff --git a/deps/lightrec/lightrec.h b/deps/lightrec/lightrec.h index 4f51e1f6..3ea8e656 100644 --- a/deps/lightrec/lightrec.h +++ b/deps/lightrec/lightrec.h @@ -47,6 +47,7 @@ struct lightrec_mem_map; #define LIGHTREC_EXIT_BREAK (1 << 1) #define LIGHTREC_EXIT_SYSCALL (1 << 2) #define LIGHTREC_EXIT_SEGFAULT (1 << 3) +#define LIGHTREC_EXIT_NOMEM (1 << 4) enum psx_map { PSX_MAP_KERNEL_USER_RAM, diff --git a/deps/lightrec/memmanager.c b/deps/lightrec/memmanager.c index d39b669f..c7502cdb 100644 --- a/deps/lightrec/memmanager.c +++ b/deps/lightrec/memmanager.c @@ -8,9 +8,6 @@ #include "memmanager.h" #include -#if ENABLE_TINYMM -#include -#endif #ifdef ENABLE_THREADED_COMPILER #include @@ -67,12 +64,7 @@ void * lightrec_malloc(struct lightrec_state *state, { void *ptr; -#if ENABLE_TINYMM - if (type == MEM_FOR_IR) - ptr = tinymm_malloc(state->tinymm, len); - else -#endif - ptr = malloc(len); + ptr = malloc(len); if (!ptr) return NULL; @@ -86,12 +78,7 @@ void * lightrec_calloc(struct lightrec_state *state, { void *ptr; -#if ENABLE_TINYMM - if (type == MEM_FOR_IR) - ptr = tinymm_zalloc(state->tinymm, len); - else -#endif - ptr = calloc(1, len); + ptr = calloc(1, len); if (!ptr) return NULL; @@ -104,12 +91,7 @@ void lightrec_free(struct lightrec_state *state, enum mem_type type, unsigned int len, void *ptr) { lightrec_unregister(type, len); -#if ENABLE_TINYMM - if (type == MEM_FOR_IR) - tinymm_free(state->tinymm, ptr); - else -#endif - free(ptr); + free(ptr); } float lightrec_get_average_ipi(void) diff --git a/deps/lightrec/optimizer.c b/deps/lightrec/optimizer.c index 562f7e00..8ee66ad0 100644 --- a/deps/lightrec/optimizer.c +++ b/deps/lightrec/optimizer.c @@ -922,7 +922,8 @@ static int lightrec_transform_ops(struct lightrec_state *state, struct block *bl break; case OP_LUI: - lightrec_modify_lui(block, i); + if (!prev || !has_delay_slot(prev->c)) + lightrec_modify_lui(block, i); lightrec_remove_useless_lui(block, i, known, values); break; @@ -965,16 +966,19 @@ static int lightrec_transform_ops(struct lightrec_state *state, struct block *bl op->i.op = OP_META_MOV; op->r.rs = op->r.rt; } - case OP_SPECIAL_SUB: /* fall-through */ + fallthrough; + case OP_SPECIAL_SUB: case OP_SPECIAL_SUBU: if (op->r.rt == 0) { pr_debug("Convert OR/ADD/SUB $zero to MOV\n"); op->i.op = OP_META_MOV; } - default: /* fall-through */ + fallthrough; + default: break; } - default: /* fall-through */ + fallthrough; + default: break; } } @@ -1015,13 +1019,16 @@ static int lightrec_switch_delay_slots(struct lightrec_state *state, struct bloc if (opcode_reads_register(next_op, op.r.rd) || opcode_writes_register(next_op, op.r.rd)) continue; - case OP_SPECIAL_JR: /* fall-through */ + fallthrough; + case OP_SPECIAL_JR: if (opcode_writes_register(next_op, op.r.rs)) continue; - default: /* fall-through */ + fallthrough; + default: break; } - case OP_J: /* fall-through */ + fallthrough; + case OP_J: break; case OP_JAL: if (opcode_reads_register(next_op, 31) || @@ -1033,7 +1040,8 @@ static int lightrec_switch_delay_slots(struct lightrec_state *state, struct bloc case OP_BNE: if (op.i.rt && opcode_writes_register(next_op, op.i.rt)) continue; - case OP_BLEZ: /* fall-through */ + fallthrough; + case OP_BLEZ: case OP_BGTZ: if (op.i.rs && opcode_writes_register(next_op, op.i.rs)) continue; @@ -1045,14 +1053,16 @@ static int lightrec_switch_delay_slots(struct lightrec_state *state, struct bloc if (opcode_reads_register(next_op, 31) || opcode_writes_register(next_op, 31)) continue; - case OP_REGIMM_BLTZ: /* fall-through */ + fallthrough; + case OP_REGIMM_BLTZ: case OP_REGIMM_BGEZ: if (op.i.rs && opcode_writes_register(next_op, op.i.rs)) continue; break; } - default: /* fall-through */ + fallthrough; + default: break; } @@ -1163,7 +1173,8 @@ static int lightrec_local_branches(struct lightrec_state *state, struct block *b offset = i + 1 + (s16)list->i.imm; if (offset >= 0 && offset < block->nb_ops) break; - default: /* fall-through */ + fallthrough; + default: continue; } @@ -1313,7 +1324,8 @@ static int lightrec_flag_io(struct lightrec_state *state, struct block *block) list->flags |= LIGHTREC_SMC; } } - case OP_SWL: /* fall-through */ + fallthrough; + case OP_SWL: case OP_SWR: case OP_SWC2: case OP_LB: @@ -1333,7 +1345,7 @@ static int lightrec_flag_io(struct lightrec_state *state, struct block *block) case PSX_MAP_KERNEL_USER_RAM: if (val == kunseg_val) list->flags |= LIGHTREC_NO_MASK; - /* fall-through */ + fallthrough; case PSX_MAP_MIRROR1: case PSX_MAP_MIRROR2: case PSX_MAP_MIRROR3: @@ -1359,7 +1371,8 @@ static int lightrec_flag_io(struct lightrec_state *state, struct block *block) break; } } - default: /* fall-through */ + fallthrough; + default: break; } } @@ -1479,7 +1492,7 @@ static u8 get_mfhi_mflo_reg(const struct block *block, u16 offset, break; } - /* fall-through */ + fallthrough; default: continue; } @@ -1530,7 +1543,7 @@ static void lightrec_replace_lo_hi(struct block *block, u16 offset, return; } - /* fall-through */ + fallthrough; default: break; } @@ -1572,7 +1585,8 @@ static int lightrec_flag_mults_divs(struct lightrec_state *state, struct block * if (lightrec_always_skip_div_check() || (known & BIT(list->c.r.rt) && values[list->c.r.rt])) list->flags |= LIGHTREC_NO_DIV_CHECK; - case OP_SPECIAL_MULT: /* fall-through */ + fallthrough; + case OP_SPECIAL_MULT: case OP_SPECIAL_MULTU: break; default: diff --git a/deps/lightrec/recompiler.c b/deps/lightrec/recompiler.c index 966635d4..4ddc522b 100644 --- a/deps/lightrec/recompiler.c +++ b/deps/lightrec/recompiler.c @@ -3,10 +3,12 @@ * Copyright (C) 2019-2021 Paul Cercueil */ +#include "blockcache.h" #include "debug.h" #include "interpreter.h" #include "lightrec-private.h" #include "memmanager.h" +#include "reaper.h" #include "slist.h" #include @@ -35,9 +37,11 @@ struct recompiler { pthread_cond_t cond; pthread_cond_t cond2; pthread_mutex_t mutex; - bool stop; + bool stop, must_flush; struct slist_elm slist; + pthread_mutex_t alloc_mutex; + unsigned int nb_recs; struct recompiler_thd thds[]; }; @@ -75,6 +79,58 @@ static struct slist_elm * lightrec_get_first_elm(struct slist_elm *head) return NULL; } +static bool lightrec_cancel_block_rec(struct recompiler *rec, + struct block_rec *block_rec) +{ + if (block_rec->compiling) { + /* Block is being recompiled - wait for + * completion */ + pthread_cond_wait(&rec->cond2, &rec->mutex); + + /* We can't guarantee the signal was for us. + * Since block_rec may have been removed while + * we were waiting on the condition, we cannot + * check block_rec->compiling again. The best + * thing is just to restart the function. */ + return false; + } + + /* Block is not yet being processed - remove it from the list */ + slist_remove(&rec->slist, &block_rec->slist); + lightrec_free(rec->state, MEM_FOR_LIGHTREC, + sizeof(*block_rec), block_rec); + + return true; +} + +static void lightrec_cancel_list(struct recompiler *rec) +{ + struct block_rec *block_rec; + struct slist_elm *next; + + while (!!(next = lightrec_get_first_elm(&rec->slist))) { + block_rec = container_of(next, struct block_rec, slist); + + lightrec_cancel_block_rec(rec, block_rec); + } + + pthread_cond_broadcast(&rec->cond2); +} + +static void lightrec_flush_code_buffer(struct lightrec_state *state, void *d) +{ + struct recompiler *rec = d; + + pthread_mutex_lock(&rec->mutex); + + if (rec->must_flush) { + lightrec_remove_outdated_blocks(state->block_cache, NULL); + rec->must_flush = false; + } + + pthread_mutex_unlock(&rec->mutex); +} + static void lightrec_compile_list(struct recompiler *rec, struct recompiler_thd *thd) { @@ -92,6 +148,21 @@ static void lightrec_compile_list(struct recompiler *rec, if (likely(!(block->flags & BLOCK_IS_DEAD))) { ret = lightrec_compile_block(thd->cstate, block); + if (ret == -ENOMEM) { + /* Code buffer is full. Request the reaper to + * flush it. */ + + pthread_mutex_lock(&rec->mutex); + if (!rec->must_flush) { + lightrec_reaper_add(rec->state->reaper, + lightrec_flush_code_buffer, + rec); + lightrec_cancel_list(rec); + rec->must_flush = true; + } + return; + } + if (ret) { pr_err("Unable to compile block at PC 0x%x: %d\n", block->pc, ret); @@ -162,6 +233,7 @@ struct recompiler *lightrec_recompiler_init(struct lightrec_state *state) rec->state = state; rec->stop = false; + rec->must_flush = false; rec->nb_recs = nb_recs; slist_init(&rec->slist); @@ -177,10 +249,16 @@ struct recompiler *lightrec_recompiler_init(struct lightrec_state *state) goto err_cnd_destroy; } + ret = pthread_mutex_init(&rec->alloc_mutex, NULL); + if (ret) { + pr_err("Cannot init alloc mutex variable: %d\n", ret); + goto err_cnd2_destroy; + } + ret = pthread_mutex_init(&rec->mutex, NULL); if (ret) { pr_err("Cannot init mutex variable: %d\n", ret); - goto err_cnd2_destroy; + goto err_alloc_mtx_destroy; } for (i = 0; i < nb_recs; i++) { @@ -199,6 +277,8 @@ struct recompiler *lightrec_recompiler_init(struct lightrec_state *state) err_mtx_destroy: pthread_mutex_destroy(&rec->mutex); +err_alloc_mtx_destroy: + pthread_mutex_destroy(&rec->alloc_mutex); err_cnd2_destroy: pthread_cond_destroy(&rec->cond2); err_cnd_destroy: @@ -221,6 +301,7 @@ void lightrec_free_recompiler(struct recompiler *rec) /* Stop the thread */ pthread_mutex_lock(&rec->mutex); pthread_cond_broadcast(&rec->cond); + lightrec_cancel_list(rec); pthread_mutex_unlock(&rec->mutex); for (i = 0; i < rec->nb_recs; i++) @@ -230,6 +311,7 @@ void lightrec_free_recompiler(struct recompiler *rec) lightrec_free_cstate(rec->thds[i].cstate); pthread_mutex_destroy(&rec->mutex); + pthread_mutex_destroy(&rec->alloc_mutex); pthread_cond_destroy(&rec->cond); pthread_cond_destroy(&rec->cond2); lightrec_free(rec->state, MEM_FOR_LIGHTREC, sizeof(*rec), rec); @@ -243,6 +325,12 @@ int lightrec_recompiler_add(struct recompiler *rec, struct block *block) pthread_mutex_lock(&rec->mutex); + /* If the recompiler must flush the code cache, we can't add the new + * job. It will be re-added next time the block's address is jumped to + * again. */ + if (rec->must_flush) + goto out_unlock; + /* If the block is marked as dead, don't compile it, it will be removed * as soon as it's safe. */ if (block->flags & BLOCK_IS_DEAD) @@ -312,28 +400,11 @@ void lightrec_recompiler_remove(struct recompiler *rec, struct block *block) for (elm = slist_first(&rec->slist); elm; elm = elm->next) { block_rec = container_of(elm, struct block_rec, slist); - if (block_rec->block != block) - continue; - - if (block_rec->compiling) { - /* Block is being recompiled - wait for - * completion */ - pthread_cond_wait(&rec->cond2, &rec->mutex); + if (block_rec->block == block) { + if (lightrec_cancel_block_rec(rec, block_rec)) + goto out_unlock; - /* We can't guarantee the signal was for us. - * Since block_rec may have been removed while - * we were waiting on the condition, we cannot - * check block_rec->compiling again. The best - * thing is just to restart the function. */ break; - } else { - /* Block is not yet being processed - remove it - * from the list */ - slist_remove(&rec->slist, elm); - lightrec_free(rec->state, MEM_FOR_LIGHTREC, - sizeof(*block_rec), block_rec); - - goto out_unlock; } } @@ -402,3 +473,13 @@ void * lightrec_recompiler_run_first_pass(struct lightrec_state *state, return NULL; } + +void lightrec_code_alloc_lock(struct lightrec_state *state) +{ + pthread_mutex_lock(&state->rec->alloc_mutex); +} + +void lightrec_code_alloc_unlock(struct lightrec_state *state) +{ + pthread_mutex_unlock(&state->rec->alloc_mutex); +} diff --git a/deps/lightrec/recompiler.h b/deps/lightrec/recompiler.h index 9bc522d1..b9fc5798 100644 --- a/deps/lightrec/recompiler.h +++ b/deps/lightrec/recompiler.h @@ -18,4 +18,7 @@ void lightrec_recompiler_remove(struct recompiler *rec, struct block *block); void * lightrec_recompiler_run_first_pass(struct lightrec_state *state, struct block *block, u32 *pc); +void lightrec_code_alloc_lock(struct lightrec_state *state); +void lightrec_code_alloc_unlock(struct lightrec_state *state); + #endif /* __LIGHTREC_RECOMPILER_H__ */ diff --git a/include/lightrec/lightrec-config.h b/include/lightrec/lightrec-config.h index 34ac7a6e..2fa750f4 100644 --- a/include/lightrec/lightrec-config.h +++ b/include/lightrec/lightrec-config.h @@ -9,8 +9,7 @@ #define ENABLE_THREADED_COMPILER 1 #define ENABLE_FIRST_PASS 1 #define ENABLE_DISASSEMBLER 0 -#define ENABLE_TINYMM 0 -#define ENABLE_CODE_BUFFER 0 +#define ENABLE_CODE_BUFFER 1 #define HAS_DEFAULT_ELM 1 diff --git a/libpcsxcore/lightrec/mem.c b/libpcsxcore/lightrec/mem.c index c3a1421e..ff67e3d0 100644 --- a/libpcsxcore/lightrec/mem.c +++ b/libpcsxcore/lightrec/mem.c @@ -31,6 +31,8 @@ #define MFD_HUGETLB 0x0004 #endif +void *code_buffer; + static const uintptr_t supported_io_bases[] = { 0x0, 0x10000000, @@ -171,8 +173,22 @@ int lightrec_init_mmap(void) psxH = (s8 *)map; + map = mmap_huge((void *)(base + 0x800000), CODE_BUFFER_SIZE, + PROT_EXEC | PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_FIXED_NOREPLACE | MAP_ANONYMOUS, + 0, 0); + if (map == MAP_FAILED) { + err = -EINVAL; + fprintf(stderr, "Unable to mmap code buffer\n"); + goto err_unmap_scratch; + } + + code_buffer = map; + return 0; +err_unmap_scratch: + munmap(psxH, 0x10000); err_unmap_bios: munmap(psxR, 0x80000); err_unmap_parallel: @@ -187,6 +203,7 @@ void lightrec_free_mmap(void) { unsigned int i; + munmap(code_buffer, CODE_BUFFER_SIZE); munmap(psxH, 0x10000); munmap(psxR, 0x80000); munmap(psxP, 0x10000); diff --git a/libpcsxcore/lightrec/mem.h b/libpcsxcore/lightrec/mem.h index 7f04ce59..d747c174 100644 --- a/libpcsxcore/lightrec/mem.h +++ b/libpcsxcore/lightrec/mem.h @@ -6,6 +6,10 @@ #ifndef __LIGHTREC_MEM_H__ #define __LIGHTREC_MEM_H__ +#define CODE_BUFFER_SIZE (8 * 1024 * 1024) + +extern void *code_buffer; + int lightrec_init_mmap(void); void lightrec_free_mmap(void); diff --git a/libpcsxcore/lightrec/plugin.c b/libpcsxcore/lightrec/plugin.c index 4b28c566..00d9c558 100644 --- a/libpcsxcore/lightrec/plugin.c +++ b/libpcsxcore/lightrec/plugin.c @@ -15,6 +15,8 @@ #include "../frontend/main.h" +#include "mem.h" + #if (defined(__arm__) || defined(__aarch64__)) && !defined(ALLOW_LIGHTREC_ON_ARM) #error "Lightrec should not be used on ARM (please specify DYNAREC=ari64 to make)" #endif @@ -289,6 +291,9 @@ static struct lightrec_mem_map lightrec_map[] = { .length = 0x200000, .mirror_of = &lightrec_map[PSX_MAP_KERNEL_USER_RAM], }, + [PSX_MAP_CODE_BUFFER] = { + .length = CODE_BUFFER_SIZE, + }, }; static void lightrec_enable_ram(struct lightrec_state *state, bool enable) @@ -315,6 +320,7 @@ static int lightrec_plugin_init(void) lightrec_map[PSX_MAP_MIRROR1].address = psxM + 0x200000; lightrec_map[PSX_MAP_MIRROR2].address = psxM + 0x400000; lightrec_map[PSX_MAP_MIRROR3].address = psxM + 0x600000; + lightrec_map[PSX_MAP_CODE_BUFFER].address = code_buffer; } lightrec_debug = !!getenv("LIGHTREC_DEBUG");