git subrepo pull --force deps/lightrec
authorPaul Cercueil <paul@crapouillou.net>
Sat, 4 Jun 2022 20:02:05 +0000 (21:02 +0100)
committerPaul Cercueil <paul@crapouillou.net>
Sat, 4 Jun 2022 20:50:19 +0000 (21:50 +0100)
subrepo:
  subdir:   "deps/lightrec"
  merged:   "a8fe7568"
upstream:
  origin:   "https://github.com/pcercuei/lightrec.git"
  branch:   "master"
  commit:   "a8fe7568"
git-subrepo:
  version:  "0.4.3"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "2f68596"

13 files changed:
deps/lightrec/.gitrepo
deps/lightrec/CMakeLists.txt
deps/lightrec/blockcache.c
deps/lightrec/blockcache.h
deps/lightrec/disassembler.c
deps/lightrec/lightrec-config.h.cmakein
deps/lightrec/lightrec-private.h
deps/lightrec/lightrec.c
deps/lightrec/lightrec.h
deps/lightrec/memmanager.c
deps/lightrec/optimizer.c
deps/lightrec/recompiler.c
deps/lightrec/recompiler.h

index c9d423a..809f13b 100644 (file)
@@ -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
index 7b28518..aa8440b 100644 (file)
@@ -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)
 
index 2182f29..70c5aeb 100644 (file)
@@ -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);
 }
 
index 3b782f4..2e55ff6 100644 (file)
@@ -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__ */
index cb332c6..9b2dbd5 100644 (file)
@@ -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));
index f5524e9..1188665 100644 (file)
@@ -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
index 87565a6..fe89e66 100644 (file)
@@ -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;
index d172a30..ba734ad 100644 (file)
@@ -27,9 +27,6 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <string.h>
-#if ENABLE_TINYMM
-#include <tinymm.h>
-#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)
index 4f51e1f..3ea8e65 100644 (file)
@@ -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,
index d39b669..c7502cd 100644 (file)
@@ -8,9 +8,6 @@
 #include "memmanager.h"
 
 #include <stdlib.h>
-#if ENABLE_TINYMM
-#include <tinymm.h>
-#endif
 
 #ifdef ENABLE_THREADED_COMPILER
 #include <stdatomic.h>
@@ -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)
index 562f7e0..8ee66ad 100644 (file)
@@ -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:
index 966635d..4ddc522 100644 (file)
@@ -3,10 +3,12 @@
  * Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
  */
 
+#include "blockcache.h"
 #include "debug.h"
 #include "interpreter.h"
 #include "lightrec-private.h"
 #include "memmanager.h"
+#include "reaper.h"
 #include "slist.h"
 
 #include <errno.h>
@@ -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);
+}
index 9bc522d..b9fc579 100644 (file)
@@ -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__ */