Update deps/lightrec
authornegativeExponent <negativeExponent@users.noreply.github.com>
Wed, 20 May 2020 16:16:51 +0000 (00:16 +0800)
committernegativeExponent <negativeExponent@users.noreply.github.com>
Wed, 20 May 2020 16:17:25 +0000 (00:17 +0800)
git subrepo clone --branch=master --force https://github.com/pcercuei/lightrec.git deps/lightrec

subrepo:
  subdir:   "deps/lightrec"
  merged:   "2081869"
upstream:
  origin:   "https://github.com/pcercuei/lightrec.git"
  branch:   "master"
  commit:   "2081869"
git-subrepo:
  version:  "0.4.1"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "a04d8c2"

24 files changed:
deps/lightrec/.gitrepo
deps/lightrec/CMakeLists.txt
deps/lightrec/README [deleted file]
deps/lightrec/README.md [new file with mode: 0644]
deps/lightrec/blockcache.c
deps/lightrec/blockcache.h
deps/lightrec/debug.h
deps/lightrec/disassembler.h
deps/lightrec/emitter.c
deps/lightrec/emitter.h
deps/lightrec/interpreter.c
deps/lightrec/interpreter.h
deps/lightrec/lightrec-private.h
deps/lightrec/lightrec.c
deps/lightrec/lightrec.h
deps/lightrec/memmanager.h
deps/lightrec/optimizer.c
deps/lightrec/optimizer.h
deps/lightrec/reaper.c [new file with mode: 0644]
deps/lightrec/reaper.h [moved from deps/lightrec/config.h with 52% similarity]
deps/lightrec/recompiler.c
deps/lightrec/recompiler.h
deps/lightrec/regcache.h
deps/lightrec/slist.h [new file with mode: 0644]

index 871f638..0dbefe8 100644 (file)
@@ -6,7 +6,7 @@
 [subrepo]
        remote = https://github.com/pcercuei/lightrec.git
        branch = master
-       commit = 6c69e104d0827e45b8c094d6a61f95c96e9efb15
-       parent = b7ee664796db949b417754d11d4ae405cf5144a5
+       commit = 2081869a00371dac285836fb950f8cd0c26b55b9
+       parent = 5c00ea32a0eab812299b08acd14c25bf6ba4ca7a
        method = merge
        cmdver = 0.4.1
index 6ac5cd4..c58dac5 100644 (file)
@@ -53,7 +53,7 @@ option(ENABLE_FIRST_PASS "Run the interpreter as first-pass optimization" ON)
 
 option(ENABLE_THREADED_COMPILER "Enable threaded compiler" ON)
 if (ENABLE_THREADED_COMPILER)
-       list(APPEND LIGHTREC_SOURCES recompiler.c)
+       list(APPEND LIGHTREC_SOURCES recompiler.c reaper.c)
 
        if (NOT ENABLE_FIRST_PASS)
                message(SEND_ERROR "Threaded compiler requires first-pass optimization")
diff --git a/deps/lightrec/README b/deps/lightrec/README
deleted file mode 100644 (file)
index 5bc4627..0000000
+++ /dev/null
@@ -1 +0,0 @@
-LightRec is my attempt at creating a dynamic recompiler for MIPS and powered by GNU Lightning.
diff --git a/deps/lightrec/README.md b/deps/lightrec/README.md
new file mode 100644 (file)
index 0000000..40ecc8f
--- /dev/null
@@ -0,0 +1,53 @@
+
+# Lightrec
+
+Lightrec is a MIPS-to-everything dynamic recompiler for
+PlayStation emulators, using
+[GNU Lightning](https://www.gnu.org/software/lightning/)
+as the code emitter.
+
+As such, in theory it should be able to run on every CPU that Lightning
+can generate code for; including, but not limited to, __x86__, __x86_64__,
+__ARM__, __Aarch64__, __MIPS__, __PowerPC__ and __Risc-V__.
+
+## Features
+
+* __High-level optimizations__.  The MIPS code is first pre-compiled into
+a form of Intermediate Representation (IR).
+Basically, just a single-linked list of structures representing the
+instructions. On that list, several optimization steps are performed:
+instructions are modified, reordered, tagged; new meta-instructions
+can be added, for instance to tell the code generator that a certain
+register won't be used anymore.
+
+* __Lazy compilation__.
+If Lightrec detects a block of code that would be very hard to
+compile properly (e.g. a branch with a branch in its delay slot),
+the block is marked as not compilable, and will always be emulated
+with the built-in interpreter. This allows to keep the code emitter
+simple and easy to understand.
+
+* __Run-time profiling__.
+The generated code will gather run-time information about the I/O access
+(whether they hit RAM, or hardware registers).
+The code generator will then use this information to generate direct
+read/writes to the emulated memories, instead of jumping to C for
+every call.
+
+* __Threaded compilation__.
+When entering a loading zone, where a lot of code has to be compiled,
+we don't want the compilation process to slow down the pace of emulation.
+To avoid that, the code compiler optionally runs on a thread, and the
+main loop will emulate the blocks that have not been compiled yet with
+the interpreter. This helps to drastically reduce the stutter that
+typically happens when a lot of new code is run.
+
+## Emulators
+
+Lightrec has been ported to the following emulators:
+
+* [__PCSX-ReArmed__ (my own fork)](https://github.com/pcercuei/pcsx_rearmed)
+
+* [__pcsx4all__ (my own fork)](https://github.com/pcercuei/pcsx4all)
+
+* [__Beetle__ (libretro)](https://github.com/libretro/beetle-psx-libretro/)
\ No newline at end of file
index 833a8e1..4263431 100644 (file)
@@ -60,12 +60,6 @@ void remove_from_code_lut(struct blockcache *cache, struct block *block)
 
 }
 
-void lightrec_mark_for_recompilation(struct blockcache *cache,
-                                    struct block *block)
-{
-       block->flags |= BLOCK_SHOULD_RECOMPILE;
-}
-
 void lightrec_register_block(struct blockcache *cache, struct block *block)
 {
        u32 pc = kunseg(block->pc);
@@ -85,8 +79,6 @@ void lightrec_unregister_block(struct blockcache *cache, struct block *block)
        u32 pc = kunseg(block->pc);
        struct block *old = cache->lut[(pc >> 2) & (LUT_SIZE - 1)];
 
-       remove_from_code_lut(cache, block);
-
        if (old == block) {
                cache->lut[(pc >> 2) & (LUT_SIZE - 1)] = old->next;
                return;
index 0c57ffc..ff63651 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -29,7 +29,4 @@ void lightrec_free_block_cache(struct blockcache *cache);
 u32 lightrec_calculate_block_hash(const struct block *block);
 _Bool lightrec_block_is_outdated(struct block *block);
 
-void lightrec_mark_for_recompilation(struct blockcache *cache,
-                                    struct block *block);
-
 #endif /* __BLOCKCACHE_H__ */
index 4048d43..4facc22 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index e4c4403..249d094 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index b09dc94..206a45c 100644 (file)
@@ -1250,7 +1250,8 @@ static void rec_mtc(const struct block *block, const struct opcode *op, u32 pc)
 
        lightrec_regcache_mark_live(reg_cache, _jit);
 
-       if (op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
+       if (op->i.op == OP_CP0 && !(op->flags & LIGHTREC_NO_DS) &&
+           (op->r.rd == 12 || op->r.rd == 13))
                lightrec_emit_end_of_block(block, op, pc, -1, pc + 4, 0, 0, true);
 }
 
@@ -1427,7 +1428,7 @@ static void rec_meta_sync(const struct block *block,
                 op->offset << 2);
        target = &state->targets[state->nb_targets++];
        target->offset = op->offset;
-       target->label = jit_label();
+       target->label = jit_indirect();
 }
 
 static const lightrec_rec_func_t rec_standard[64] = {
index 57ededf..ec3fc78 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index acc41ea..f586685 100644 (file)
@@ -150,8 +150,8 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                 * but on branch boundaries, we need to adjust the return
                 * address so that the GTE opcode is effectively executed.
                 */
-               cause = (*state->ops.cop0_ops.cfc)(state, 13);
-               epc = (*state->ops.cop0_ops.cfc)(state, 14);
+               cause = (*state->ops.cop0_ops.cfc)(state, op->c.opcode, 13);
+               epc = (*state->ops.cop0_ops.cfc)(state, op->c.opcode, 14);
 
                if (!(cause & 0x7c) && epc == pc - 4)
                        pc -= 4;
@@ -218,6 +218,8 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                        branch_taken = is_branch_taken(reg_cache, op_next);
                        pr_debug("Target of impossible branch is a branch, "
                                 "%staken.\n", branch_taken ? "" : "not ");
+                       inter->cycles += lightrec_cycles_of_opcode(op_next);
+                       old_rs = reg_cache[op_next.r.rs];
                } else {
                        new_op.c = op_next;
                        new_op.flags = 0;
@@ -248,16 +250,24 @@ static u32 int_delay_slot(struct interpreter *inter, u32 pc, bool branch)
                new_rt = reg_cache[op->r.rt];
 
        /* Execute delay slot opcode */
-       if (branch_at_addr)
-               ds_next_pc = int_branch(&inter2, pc, op_next, branch_taken);
-       else
-               ds_next_pc = (*int_standard[inter2.op->i.op])(&inter2);
+       ds_next_pc = (*int_standard[inter2.op->i.op])(&inter2);
+
+       if (branch_at_addr) {
+               if (op_next.i.op == OP_SPECIAL)
+                       /* TODO: Handle JALR setting $ra */
+                       ds_next_pc = old_rs;
+               else if (op_next.i.op == OP_J || op_next.i.op == OP_JAL)
+                       /* TODO: Handle JAL setting $ra */
+                       ds_next_pc = (pc & 0xf0000000) | (op_next.j.imm << 2);
+               else
+                       ds_next_pc = pc + 4 + ((s16)op_next.i.imm << 2);
+       }
 
        if (branch_at_addr && !branch_taken) {
                /* If the branch at the target of the branch opcode is not
                 * taken, we jump to its delay slot */
                next_pc = pc + sizeof(u32);
-       } else if (!branch && branch_in_ds) {
+       } else if (branch_at_addr || (!branch && branch_in_ds)) {
                next_pc = ds_next_pc;
        }
 
@@ -475,7 +485,8 @@ static u32 int_ctc(struct interpreter *inter)
        /* If we have a MTC0 or CTC0 to CP0 register 12 (Status) or 13 (Cause),
         * return early so that the emulator will be able to check software
         * interrupt status. */
-       if (op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
+       if (!(inter->op->flags & LIGHTREC_NO_DS) &&
+           op->i.op == OP_CP0 && (op->r.rd == 12 || op->r.rd == 13))
                return inter->block->pc + (op->offset + 1) * sizeof(u32);
        else
                return jump_next(inter);
@@ -487,13 +498,13 @@ static u32 int_cp0_RFE(struct interpreter *inter)
        u32 status;
 
        /* Read CP0 Status register (r12) */
-       status = state->ops.cop0_ops.mfc(state, 12);
+       status = state->ops.cop0_ops.mfc(state, inter->op->c.opcode, 12);
 
        /* Switch the bits */
        status = ((status & 0x3c) >> 2) | (status & ~0xf);
 
        /* Write it back */
-       state->ops.cop0_ops.ctc(state, 12, status);
+       state->ops.cop0_ops.ctc(state, inter->op->c.opcode, 12, status);
 
        return jump_next(inter);
 }
index d4177b3..2113779 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index 4c9c269..6304515 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2016-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -50,6 +50,7 @@
 #define BLOCK_NEVER_COMPILE    BIT(0)
 #define BLOCK_SHOULD_RECOMPILE BIT(1)
 #define BLOCK_FULLY_TAGGED     BIT(2)
+#define BLOCK_IS_DEAD          BIT(3)
 
 #define RAM_SIZE       0x200000
 #define BIOS_SIZE      0x80000
@@ -66,6 +67,7 @@ struct recompiler;
 struct regcache;
 struct opcode;
 struct tinymm;
+struct reaper;
 
 struct block {
        jit_state_t *_jit;
@@ -115,9 +117,11 @@ struct lightrec_state {
        struct blockcache *block_cache;
        struct regcache *reg_cache;
        struct recompiler *rec;
+       struct reaper *reaper;
        void (*eob_wrapper_func)(void);
        void (*get_next_block)(void);
        struct lightrec_ops ops;
+       unsigned int nb_precompile;
        unsigned int cycles;
        unsigned int nb_maps;
        const struct lightrec_mem_map *maps;
index 47c49c8..7fdf74a 100644 (file)
@@ -20,6 +20,7 @@
 #include "interpreter.h"
 #include "lightrec.h"
 #include "memmanager.h"
+#include "reaper.h"
 #include "recompiler.h"
 #include "regcache.h"
 #include "optimizer.h"
 static struct block * lightrec_precompile_block(struct lightrec_state *state,
                                                u32 pc);
 
+static void lightrec_default_sb(struct lightrec_state *state, u32 opcode,
+                               void *host, u32 addr, u8 data)
+{
+       *(u8 *)host = data;
+
+       if (!state->invalidate_from_dma_only)
+               lightrec_invalidate(state, addr, 1);
+}
+
+static void lightrec_default_sh(struct lightrec_state *state, u32 opcode,
+                               void *host, u32 addr, u16 data)
+{
+       *(u16 *)host = HTOLE16(data);
+
+       if (!state->invalidate_from_dma_only)
+               lightrec_invalidate(state, addr, 2);
+}
+
+static void lightrec_default_sw(struct lightrec_state *state, u32 opcode,
+                               void *host, u32 addr, u32 data)
+{
+       *(u32 *)host = HTOLE32(data);
+
+       if (!state->invalidate_from_dma_only)
+               lightrec_invalidate(state, addr, 4);
+}
+
+static u8 lightrec_default_lb(struct lightrec_state *state,
+                             u32 opcode, void *host, u32 addr)
+{
+       return *(u8 *)host;
+}
+
+static u16 lightrec_default_lh(struct lightrec_state *state,
+                              u32 opcode, void *host, u32 addr)
+{
+       return LE16TOH(*(u16 *)host);
+}
+
+static u32 lightrec_default_lw(struct lightrec_state *state,
+                              u32 opcode, void *host, u32 addr)
+{
+       return LE32TOH(*(u32 *)host);
+}
+
+static const struct lightrec_mem_map_ops lightrec_default_ops = {
+       .sb = lightrec_default_sb,
+       .sh = lightrec_default_sh,
+       .sw = lightrec_default_sw,
+       .lb = lightrec_default_lb,
+       .lh = lightrec_default_lh,
+       .lw = lightrec_default_lw,
+};
+
 static void __segfault_cb(struct lightrec_state *state, u32 addr)
 {
        lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
@@ -50,33 +105,94 @@ static void __segfault_cb(struct lightrec_state *state, u32 addr)
               "load/store at address 0x%08x\n", addr);
 }
 
-static u32 lightrec_rw_ops(struct lightrec_state *state, union code op,
-               const struct lightrec_mem_map_ops *ops, u32 addr, u32 data)
+static void lightrec_swl(struct lightrec_state *state,
+                        const struct lightrec_mem_map_ops *ops,
+                        u32 opcode, void *host, u32 addr, u32 data)
 {
-       switch (op.i.op) {
-       case OP_SB:
-               ops->sb(state, addr, (u8) data);
-               return 0;
-       case OP_SH:
-               ops->sh(state, addr, (u16) data);
-               return 0;
-       case OP_SWL:
-       case OP_SWR:
-       case OP_SW:
-               ops->sw(state, addr, data);
-               return 0;
-       case OP_LB:
-               return (s32) (s8) ops->lb(state, addr);
-       case OP_LBU:
-               return ops->lb(state, addr);
-       case OP_LH:
-               return (s32) (s16) ops->lh(state, addr);
-       case OP_LHU:
-               return ops->lh(state, addr);
-       case OP_LW:
-       default:
-               return ops->lw(state, addr);
-       }
+       unsigned int shift = addr & 0x3;
+       unsigned int mask = GENMASK(31, (shift + 1) * 8);
+       u32 old_data;
+
+       /* Align to 32 bits */
+       addr &= ~3;
+       host = (void *)((uintptr_t)host & ~3);
+
+       old_data = ops->lw(state, opcode, host, addr);
+
+       data = (data >> ((3 - shift) * 8)) | (old_data & mask);
+
+       ops->sw(state, opcode, host, addr, data);
+}
+
+static void lightrec_swr(struct lightrec_state *state,
+                        const struct lightrec_mem_map_ops *ops,
+                        u32 opcode, void *host, u32 addr, u32 data)
+{
+       unsigned int shift = addr & 0x3;
+       unsigned int mask = (1 << (shift * 8)) - 1;
+       u32 old_data;
+
+       /* Align to 32 bits */
+       addr &= ~3;
+       host = (void *)((uintptr_t)host & ~3);
+
+       old_data = ops->lw(state, opcode, host, addr);
+
+       data = (data << (shift * 8)) | (old_data & mask);
+
+       ops->sw(state, opcode, host, addr, data);
+}
+
+static void lightrec_swc2(struct lightrec_state *state, union code op,
+                         const struct lightrec_mem_map_ops *ops,
+                         void *host, u32 addr)
+{
+       u32 data = state->ops.cop2_ops.mfc(state, op.opcode, op.i.rt);
+
+       ops->sw(state, op.opcode, host, addr, data);
+}
+
+static u32 lightrec_lwl(struct lightrec_state *state,
+                       const struct lightrec_mem_map_ops *ops,
+                       u32 opcode, void *host, u32 addr, u32 data)
+{
+       unsigned int shift = addr & 0x3;
+       unsigned int mask = (1 << (24 - shift * 8)) - 1;
+       u32 old_data;
+
+       /* Align to 32 bits */
+       addr &= ~3;
+       host = (void *)((uintptr_t)host & ~3);
+
+       old_data = ops->lw(state, opcode, host, addr);
+
+       return (data & mask) | (old_data << (24 - shift * 8));
+}
+
+static u32 lightrec_lwr(struct lightrec_state *state,
+                       const struct lightrec_mem_map_ops *ops,
+                       u32 opcode, void *host, u32 addr, u32 data)
+{
+       unsigned int shift = addr & 0x3;
+       unsigned int mask = GENMASK(31, 32 - shift * 8);
+       u32 old_data;
+
+       /* Align to 32 bits */
+       addr &= ~3;
+       host = (void *)((uintptr_t)host & ~3);
+
+       old_data = ops->lw(state, opcode, host, addr);
+
+       return (data & mask) | (old_data >> (shift * 8));
+}
+
+static void lightrec_lwc2(struct lightrec_state *state, union code op,
+                         const struct lightrec_mem_map_ops *ops,
+                         void *host, u32 addr)
+{
+       u32 data = ops->lw(state, op.opcode, host, addr);
+
+       state->ops.cop2_ops.mtc(state, op.opcode, op.i.rt, data);
 }
 
 static void lightrec_invalidate_map(struct lightrec_state *state,
@@ -105,9 +221,9 @@ u32 lightrec_rw(struct lightrec_state *state, union code op,
                u32 addr, u32 data, u16 *flags)
 {
        const struct lightrec_mem_map *map;
-       u32 shift, mem_data, mask, pc;
-       uintptr_t new_addr;
-       u32 kaddr;
+       const struct lightrec_mem_map_ops *ops;
+       u32 kaddr, pc, opcode = op.opcode;
+       void *host;
 
        addr += (s16) op.i.imm;
        kaddr = kunseg(addr);
@@ -120,91 +236,60 @@ u32 lightrec_rw(struct lightrec_state *state, union code op,
 
        pc = map->pc;
 
+       while (map->mirror_of)
+               map = map->mirror_of;
+
+       host = (void *)((uintptr_t)map->address + kaddr - pc);
+
        if (unlikely(map->ops)) {
                if (flags)
                        *flags |= LIGHTREC_HW_IO;
 
-               return lightrec_rw_ops(state, op, map->ops, addr, data);
-       }
-
-       while (map->mirror_of)
-               map = map->mirror_of;
-
-       if (flags)
-               *flags |= LIGHTREC_DIRECT_IO;
+               ops = map->ops;
+       } else {
+               if (flags)
+                       *flags |= LIGHTREC_DIRECT_IO;
 
-       kaddr -= pc;
-       new_addr = (uintptr_t) map->address + kaddr;
+               ops = &lightrec_default_ops;
+       }
 
        switch (op.i.op) {
        case OP_SB:
-               *(u8 *) new_addr = (u8) data;
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr);
+               ops->sb(state, opcode, host, addr, (u8) data);
                return 0;
        case OP_SH:
-               *(u16 *) new_addr = HTOLE16((u16) data);
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr);
+               ops->sh(state, opcode, host, addr, (u16) data);
                return 0;
        case OP_SWL:
-               shift = kaddr & 3;
-               mem_data = LE32TOH(*(u32 *)(new_addr & ~3));
-               mask = GENMASK(31, (shift + 1) * 8);
-
-               *(u32 *)(new_addr & ~3) = HTOLE32((data >> ((3 - shift) * 8))
-                                                 | (mem_data & mask));
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr & ~0x3);
+               lightrec_swl(state, ops, opcode, host, addr, data);
                return 0;
        case OP_SWR:
-               shift = kaddr & 3;
-               mem_data = LE32TOH(*(u32 *)(new_addr & ~3));
-               mask = (1 << (shift * 8)) - 1;
-
-               *(u32 *)(new_addr & ~3) = HTOLE32((data << (shift * 8))
-                                                 | (mem_data & mask));
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr & ~0x3);
+               lightrec_swr(state, ops, opcode, host, addr, data);
                return 0;
        case OP_SW:
-               *(u32 *) new_addr = HTOLE32(data);
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr);
+               ops->sw(state, opcode, host, addr, data);
                return 0;
        case OP_SWC2:
-               *(u32 *) new_addr = HTOLE32(state->ops.cop2_ops.mfc(state,
-                                                                   op.i.rt));
-               if (!state->invalidate_from_dma_only)
-                       lightrec_invalidate_map(state, map, kaddr);
+               lightrec_swc2(state, op, ops, host, addr);
                return 0;
        case OP_LB:
-               return (s32) *(s8 *) new_addr;
+               return (s32) (s8) ops->lb(state, opcode, host, addr);
        case OP_LBU:
-               return *(u8 *) new_addr;
+               return ops->lb(state, opcode, host, addr);
        case OP_LH:
-               return (s32)(s16) LE16TOH(*(u16 *) new_addr);
+               return (s32) (s16) ops->lh(state, opcode, host, addr);
        case OP_LHU:
-               return LE16TOH(*(u16 *) new_addr);
-       case OP_LWL:
-               shift = kaddr & 3;
-               mem_data = LE32TOH(*(u32 *)(new_addr & ~3));
-               mask = (1 << (24 - shift * 8)) - 1;
-
-               return (data & mask) | (mem_data << (24 - shift * 8));
-       case OP_LWR:
-               shift = kaddr & 3;
-               mem_data = LE32TOH(*(u32 *)(new_addr & ~3));
-               mask = GENMASK(31, 32 - shift * 8);
-
-               return (data & mask) | (mem_data >> (shift * 8));
+               return ops->lh(state, opcode, host, addr);
        case OP_LWC2:
-               state->ops.cop2_ops.mtc(state, op.i.rt,
-                                       LE32TOH(*(u32 *) new_addr));
+               lightrec_lwc2(state, op, ops, host, addr);
                return 0;
+       case OP_LWL:
+               return lightrec_lwl(state, ops, opcode, host, addr, data);
+       case OP_LWR:
+               return lightrec_lwr(state, ops, opcode, host, addr, data);
        case OP_LW:
        default:
-               return LE32TOH(*(u32 *) new_addr);
+               return ops->lw(state, opcode, host, addr);
        }
 }
 
@@ -247,7 +332,7 @@ static void lightrec_rw_generic_cb(struct lightrec_state *state,
                         "tagged - flag for recompilation\n",
                         block->pc, op->offset << 2);
 
-               lightrec_mark_for_recompilation(state->block_cache, block);
+               block->flags |= BLOCK_SHOULD_RECOMPILE;
        }
 }
 
@@ -255,7 +340,7 @@ u32 lightrec_mfc(struct lightrec_state *state, union code op)
 {
        bool is_cfc = (op.i.op == OP_CP0 && op.r.rs == OP_CP0_CFC0) ||
                      (op.i.op == OP_CP2 && op.r.rs == OP_CP2_BASIC_CFC2);
-       u32 (*func)(struct lightrec_state *, u8);
+       u32 (*func)(struct lightrec_state *, u32, u8);
        const struct lightrec_cop_ops *ops;
 
        if (op.i.op == OP_CP0)
@@ -268,7 +353,7 @@ u32 lightrec_mfc(struct lightrec_state *state, union code op)
        else
                func = ops->mfc;
 
-       return (*func)(state, op.r.rd);
+       return (*func)(state, op.opcode, op.r.rd);
 }
 
 static void lightrec_mfc_cb(struct lightrec_state *state, union code op)
@@ -283,7 +368,7 @@ void lightrec_mtc(struct lightrec_state *state, union code op, u32 data)
 {
        bool is_ctc = (op.i.op == OP_CP0 && op.r.rs == OP_CP0_CTC0) ||
                      (op.i.op == OP_CP2 && op.r.rs == OP_CP2_BASIC_CTC2);
-       void (*func)(struct lightrec_state *, u8, u32);
+       void (*func)(struct lightrec_state *, u32, u8, u32);
        const struct lightrec_cop_ops *ops;
 
        if (op.i.op == OP_CP0)
@@ -296,7 +381,7 @@ void lightrec_mtc(struct lightrec_state *state, union code op, u32 data)
        else
                func = ops->mtc;
 
-       (*func)(state, op.r.rd, data);
+       (*func)(state, op.opcode, op.r.rd, data);
 }
 
 static void lightrec_mtc_cb(struct lightrec_state *state, union code op)
@@ -309,13 +394,13 @@ static void lightrec_rfe_cb(struct lightrec_state *state, union code op)
        u32 status;
 
        /* Read CP0 Status register (r12) */
-       status = state->ops.cop0_ops.mfc(state, 12);
+       status = state->ops.cop0_ops.mfc(state, op.opcode, 12);
 
        /* Switch the bits */
        status = ((status & 0x3c) >> 2) | (status & ~0xf);
 
        /* Write it back */
-       state->ops.cop0_ops.ctc(state, 12, status);
+       state->ops.cop0_ops.ctc(state, op.opcode, 12, status);
 }
 
 static void lightrec_cp_cb(struct lightrec_state *state, union code op)
@@ -353,6 +438,7 @@ struct block * lightrec_get_block(struct lightrec_state *state, u32 pc)
                        lightrec_recompiler_remove(state->rec, block);
 
                lightrec_unregister_block(state->block_cache, block);
+               remove_from_code_lut(state->block_cache, block);
                lightrec_free_block(block);
                block = NULL;
        }
@@ -387,22 +473,18 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
                if (unlikely(!block))
                        return NULL;
 
-               should_recompile = block->flags & BLOCK_SHOULD_RECOMPILE;
+               should_recompile = block->flags & BLOCK_SHOULD_RECOMPILE &&
+                       !(block->flags & BLOCK_IS_DEAD);
 
                if (unlikely(should_recompile)) {
-                       pr_debug("Block at PC 0x%08x should recompile"
-                                " - freeing old code\n", pc);
-
-                       if (ENABLE_THREADED_COMPILER)
-                               lightrec_recompiler_remove(state->rec, block);
+                       pr_debug("Block at PC 0x%08x should recompile\n", pc);
 
-                       remove_from_code_lut(state->block_cache, block);
                        lightrec_unregister(MEM_FOR_CODE, block->code_size);
-                       if (block->_jit)
-                               _jit_destroy_state(block->_jit);
-                       block->_jit = NULL;
-                       block->function = NULL;
-                       block->flags &= ~BLOCK_SHOULD_RECOMPILE;
+
+                       if (ENABLE_THREADED_COMPILER)
+                               lightrec_recompiler_add(state->rec, block);
+                       else
+                               lightrec_compile_block(block);
                }
 
                if (ENABLE_THREADED_COMPILER && likely(!should_recompile))
@@ -787,6 +869,8 @@ static struct block * lightrec_precompile_block(struct lightrec_state *state,
 
        block->hash = lightrec_calculate_block_hash(block);
 
+       pr_debug("Recompile count: %u\n", state->nb_precompile++);
+
        return block;
 }
 
@@ -824,17 +908,32 @@ static bool lightrec_block_is_fully_tagged(struct block *block)
        return true;
 }
 
+static void lightrec_reap_block(void *data)
+{
+       struct block *block = data;
+
+       pr_debug("Reap dead block at PC 0x%08x\n", block->pc);
+       lightrec_free_block(block);
+}
+
+static void lightrec_reap_jit(void *data)
+{
+       _jit_destroy_state(data);
+}
+
 int lightrec_compile_block(struct block *block)
 {
        struct lightrec_state *state = block->state;
+       struct lightrec_branch_target *target;
        bool op_list_freed = false, fully_tagged = false;
+       struct block *block2;
        struct opcode *elm;
-       jit_state_t *_jit;
+       jit_state_t *_jit, *oldjit;
        jit_node_t *start_of_block;
        bool skip_next = false;
        jit_word_t code_size;
        unsigned int i, j;
-       u32 next_pc;
+       u32 next_pc, offset;
 
        fully_tagged = lightrec_block_is_fully_tagged(block);
        if (fully_tagged)
@@ -844,6 +943,7 @@ int lightrec_compile_block(struct block *block)
        if (!_jit)
                return -ENOMEM;
 
+       oldjit = block->_jit;
        block->_jit = _jit;
 
        lightrec_regcache_reset(state->reg_cache);
@@ -921,10 +1021,52 @@ int lightrec_compile_block(struct block *block)
        jit_epilog();
 
        block->function = jit_emit();
+       block->flags &= ~BLOCK_SHOULD_RECOMPILE;
 
        /* Add compiled function to the LUT */
        state->code_lut[lut_offset(block->pc)] = block->function;
 
+       /* Fill code LUT with the block's entry points */
+       for (i = 0; i < state->nb_targets; i++) {
+               target = &state->targets[i];
+
+               if (target->offset) {
+                       offset = lut_offset(block->pc) + target->offset;
+                       state->code_lut[offset] = jit_address(target->label);
+               }
+       }
+
+       /* Detect old blocks that have been covered by the new one */
+       for (i = 0; i < state->nb_targets; i++) {
+               target = &state->targets[i];
+
+               if (!target->offset)
+                       continue;
+
+               offset = block->pc + target->offset * sizeof(u32);
+               block2 = lightrec_find_block(state->block_cache, offset);
+               if (block2) {
+                       /* No need to check if block2 is compilable - it must
+                        * be, otherwise block wouldn't be compilable either */
+
+                       block2->flags |= BLOCK_IS_DEAD;
+
+                       pr_debug("Reap block 0x%08x as it's covered by block "
+                                "0x%08x\n", block2->pc, block->pc);
+
+                       lightrec_unregister_block(state->block_cache, block2);
+
+                       if (ENABLE_THREADED_COMPILER) {
+                               lightrec_recompiler_remove(state->rec, block2);
+                               lightrec_reaper_add(state->reaper,
+                                                   lightrec_reap_block,
+                                                   block2);
+                       } else {
+                               lightrec_free_block(block2);
+                       }
+               }
+       }
+
        jit_get_code(&code_size);
        lightrec_register(MEM_FOR_CODE, code_size);
 
@@ -948,6 +1090,17 @@ int lightrec_compile_block(struct block *block)
                block->opcode_list = NULL;
        }
 
+       if (oldjit) {
+               pr_debug("Block 0x%08x recompiled, reaping old jit context.\n",
+                        block->pc);
+
+               if (ENABLE_THREADED_COMPILER)
+                       lightrec_reaper_add(state->reaper,
+                                           lightrec_reap_jit, oldjit);
+               else
+                       _jit_destroy_state(oldjit);
+       }
+
        return 0;
 }
 
@@ -974,6 +1127,9 @@ u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
                state->current_cycle = state->target_cycle - cycles_delta;
        }
 
+       if (ENABLE_THREADED_COMPILER)
+               lightrec_reaper_reap(state->reaper);
+
        return state->next_pc;
 }
 
@@ -1049,6 +1205,10 @@ struct lightrec_state * lightrec_init(char *argv0,
                state->rec = lightrec_recompiler_init(state);
                if (!state->rec)
                        goto err_free_reg_cache;
+
+               state->reaper = lightrec_reaper_init(state);
+               if (!state->reaper)
+                       goto err_free_recompiler;
        }
 
        state->nb_maps = nb;
@@ -1058,7 +1218,7 @@ struct lightrec_state * lightrec_init(char *argv0,
 
        state->dispatcher = generate_dispatcher(state);
        if (!state->dispatcher)
-               goto err_free_recompiler;
+               goto err_free_reaper;
 
        state->rw_generic_wrapper = generate_wrapper(state,
                                                     lightrec_rw_generic_cb,
@@ -1137,6 +1297,9 @@ err_free_generic_rw_wrapper:
        lightrec_free_block(state->rw_generic_wrapper);
 err_free_dispatcher:
        lightrec_free_block(state->dispatcher);
+err_free_reaper:
+       if (ENABLE_THREADED_COMPILER)
+               lightrec_reaper_destroy(state->reaper);
 err_free_recompiler:
        if (ENABLE_THREADED_COMPILER)
                lightrec_free_recompiler(state->rec);
@@ -1159,8 +1322,10 @@ err_finish_jit:
 
 void lightrec_destroy(struct lightrec_state *state)
 {
-       if (ENABLE_THREADED_COMPILER)
+       if (ENABLE_THREADED_COMPILER) {
                lightrec_free_recompiler(state->rec);
+               lightrec_reaper_destroy(state->reaper);
+       }
 
        lightrec_free_regcache(state->reg_cache);
        lightrec_free_block_cache(state->block_cache);
index d3d896c..d0793c0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2016-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -78,12 +78,15 @@ enum mem_type {
 };
 
 struct lightrec_mem_map_ops {
-       void (*sb)(struct lightrec_state *, u32 addr, u8 data);
-       void (*sh)(struct lightrec_state *, u32 addr, u16 data);
-       void (*sw)(struct lightrec_state *, u32 addr, u32 data);
-       u8 (*lb)(struct lightrec_state *, u32 addr);
-       u16 (*lh)(struct lightrec_state *, u32 addr);
-       u32 (*lw)(struct lightrec_state *, u32 addr);
+       void (*sb)(struct lightrec_state *, u32 opcode,
+                  void *host, u32 addr, u8 data);
+       void (*sh)(struct lightrec_state *, u32 opcode,
+                  void *host, u32 addr, u16 data);
+       void (*sw)(struct lightrec_state *, u32 opcode,
+                  void *host, u32 addr, u32 data);
+       u8 (*lb)(struct lightrec_state *, u32 opcode, void *host, u32 addr);
+       u16 (*lh)(struct lightrec_state *, u32 opcode, void *host, u32 addr);
+       u32 (*lw)(struct lightrec_state *, u32 opcode, void *host, u32 addr);
 };
 
 struct lightrec_mem_map {
@@ -95,11 +98,11 @@ struct lightrec_mem_map {
 };
 
 struct lightrec_cop_ops {
-       u32 (*mfc)(struct lightrec_state *state, u8 reg);
-       u32 (*cfc)(struct lightrec_state *state, u8 reg);
-       void (*mtc)(struct lightrec_state *state, u8 reg, u32 value);
-       void (*ctc)(struct lightrec_state *state, u8 reg, u32 value);
-       void (*op)(struct lightrec_state *state, u32 opcode);
+       u32 (*mfc)(struct lightrec_state *state, u32 op, u8 reg);
+       u32 (*cfc)(struct lightrec_state *state, u32 op, u8 reg);
+       void (*mtc)(struct lightrec_state *state, u32 op, u8 reg, u32 value);
+       void (*ctc)(struct lightrec_state *state, u32 op, u8 reg, u32 value);
+       void (*op)(struct lightrec_state *state, u32 op);
 };
 
 struct lightrec_ops {
index 956e7c7..bd5028d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index 92b4daa..cf431f2 100644 (file)
@@ -675,7 +675,7 @@ static int lightrec_switch_delay_slots(struct block *block)
                list->c = next_op;
                list->next->c = op;
                list->next->flags = list->flags | LIGHTREC_NO_DS;
-               list->flags = flags;
+               list->flags = flags | LIGHTREC_NO_DS;
                list->offset++;
                list->next->offset--;
        }
@@ -877,11 +877,12 @@ static int lightrec_flag_stores(struct block *block)
                case OP_SB:
                case OP_SH:
                case OP_SW:
-                       /* Mark all store operations that target $sp, $gp, $k0
-                        * or $k1 as not requiring code invalidation. This is
-                        * based on the heuristic that stores using one of these
+                       /* Mark all store operations that target $sp or $gp
+                        * as not requiring code invalidation. This is based
+                        * on the heuristic that stores using one of these
                         * registers as address will never hit a code page. */
-                       if (list->i.rs >= 26 && list->i.rs <= 29) {
+                       if (list->i.rs >= 28 && list->i.rs <= 29 &&
+                           !block->state->maps[PSX_MAP_KERNEL_USER_RAM].ops) {
                                pr_debug("Flaging opcode 0x%08x as not requiring invalidation\n",
                                         list->opcode);
                                list->flags |= LIGHTREC_NO_INVALIDATE;
index d8def69..84a8fc9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
diff --git a/deps/lightrec/reaper.c b/deps/lightrec/reaper.c
new file mode 100644 (file)
index 0000000..377685c
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ */
+
+#include "blockcache.h"
+#include "debug.h"
+#include "lightrec-private.h"
+#include "memmanager.h"
+#include "slist.h"
+#include "reaper.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+
+struct reaper_elm {
+       reap_func_t func;
+       void *data;
+       struct slist_elm slist;
+};
+
+struct reaper {
+       struct lightrec_state *state;
+       pthread_mutex_t mutex;
+       struct slist_elm reap_list;
+};
+
+struct reaper *lightrec_reaper_init(struct lightrec_state *state)
+{
+       struct reaper *reaper;
+       int ret;
+
+       reaper = lightrec_malloc(state, MEM_FOR_LIGHTREC, sizeof(*reaper));
+       if (!reaper) {
+               pr_err("Cannot create reaper: Out of memory\n");
+               return NULL;
+       }
+
+       reaper->state = state;
+       slist_init(&reaper->reap_list);
+
+       ret = pthread_mutex_init(&reaper->mutex, NULL);
+       if (ret) {
+               pr_err("Cannot init mutex variable: %d\n", ret);
+               lightrec_free(reaper->state, MEM_FOR_LIGHTREC,
+                             sizeof(*reaper), reaper);
+               return NULL;
+       }
+
+       return reaper;
+}
+
+void lightrec_reaper_destroy(struct reaper *reaper)
+{
+       pthread_mutex_destroy(&reaper->mutex);
+       lightrec_free(reaper->state, MEM_FOR_LIGHTREC, sizeof(*reaper), reaper);
+}
+
+int lightrec_reaper_add(struct reaper *reaper, reap_func_t f, void *data)
+{
+       struct reaper_elm *reaper_elm;
+       struct slist_elm *elm;
+       int ret = 0;
+
+       pthread_mutex_lock(&reaper->mutex);
+
+       for (elm = reaper->reap_list.next; elm; elm = elm->next) {
+               reaper_elm = container_of(elm, struct reaper_elm, slist);
+
+               if (reaper_elm->data == data)
+                       goto out_unlock;
+       }
+
+       reaper_elm = lightrec_malloc(reaper->state, MEM_FOR_LIGHTREC,
+                                    sizeof(*reaper_elm));
+       if (!reaper_elm) {
+               pr_err("Cannot add reaper entry: Out of memory\n");
+               ret = -ENOMEM;
+               goto out_unlock;
+       }
+
+       reaper_elm->func = f;
+       reaper_elm->data = data;
+       slist_append(&reaper->reap_list, &reaper_elm->slist);
+
+out_unlock:
+       pthread_mutex_unlock(&reaper->mutex);
+       return ret;
+}
+
+void lightrec_reaper_reap(struct reaper *reaper)
+{
+       struct reaper_elm *reaper_elm;
+       struct slist_elm *elm;
+
+       pthread_mutex_lock(&reaper->mutex);
+
+       while (!!(elm = slist_first(&reaper->reap_list))) {
+               slist_remove(&reaper->reap_list, elm);
+               pthread_mutex_unlock(&reaper->mutex);
+
+               reaper_elm = container_of(elm, struct reaper_elm, slist);
+
+               (*reaper_elm->func)(reaper_elm->data);
+
+               lightrec_free(reaper->state, MEM_FOR_LIGHTREC,
+                             sizeof(*reaper_elm), reaper_elm);
+
+               pthread_mutex_lock(&reaper->mutex);
+       }
+
+       pthread_mutex_unlock(&reaper->mutex);
+}
similarity index 52%
rename from deps/lightrec/config.h
rename to deps/lightrec/reaper.h
index 64c6a3f..0309b64 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Lesser General Public License for more details.
  */
 
-#ifndef __LIGHTREC_CONFIG_H__
-#define __LIGHTREC_CONFIG_H__
+#ifndef __LIGHTREC_REAPER_H__
+#define __LIGHTREC_REAPER_H__
 
-#define ENABLE_THREADED_COMPILER 1
-#define ENABLE_FIRST_PASS 1
-#define ENABLE_DISASSEMBLER 0
-#define ENABLE_TINYMM 0
+struct lightrec_state;
+struct reaper;
 
-#endif /* __LIGHTREC_CONFIG_H__ */
+typedef void (*reap_func_t)(void *);
 
+struct reaper *lightrec_reaper_init(struct lightrec_state *state);
+void lightrec_reaper_destroy(struct reaper *reaper);
+
+int lightrec_reaper_add(struct reaper *reaper, reap_func_t f, void *data);
+void lightrec_reaper_reap(struct reaper *reaper);
+
+#endif /* __LIGHTREC_REAPER_H__ */
index 379881a..e60889c 100644 (file)
@@ -16,6 +16,7 @@
 #include "interpreter.h"
 #include "lightrec-private.h"
 #include "memmanager.h"
+#include "slist.h"
 
 #include <errno.h>
 #include <stdatomic.h>
@@ -25,7 +26,7 @@
 
 struct block_rec {
        struct block *block;
-       struct block_rec *next;
+       struct slist_elm slist;
 };
 
 struct recompiler {
@@ -35,31 +36,19 @@ struct recompiler {
        pthread_mutex_t mutex;
        bool stop;
        struct block *current_block;
-       struct block_rec *list;
+       struct slist_elm slist;
 };
 
-static void slist_remove(struct recompiler *rec, struct block_rec *elm)
-{
-       struct block_rec *prev;
-
-       if (rec->list == elm) {
-               rec->list = elm->next;
-       } else {
-               for (prev = rec->list; prev && prev->next != elm; )
-                       prev = prev->next;
-               if (prev)
-                       prev->next = elm->next;
-       }
-}
-
 static void lightrec_compile_list(struct recompiler *rec)
 {
-       struct block_rec *next;
+       struct block_rec *block_rec;
+       struct slist_elm *next;
        struct block *block;
        int ret;
 
-       while (!!(next = rec->list)) {
-               block = next->block;
+       while (!!(next = slist_first(&rec->slist))) {
+               block_rec = container_of(next, struct block_rec, slist);
+               block = block_rec->block;
                rec->current_block = block;
 
                pthread_mutex_unlock(&rec->mutex);
@@ -72,9 +61,9 @@ static void lightrec_compile_list(struct recompiler *rec)
 
                pthread_mutex_lock(&rec->mutex);
 
-               slist_remove(rec, next);
+               slist_remove(&rec->slist, next);
                lightrec_free(rec->state, MEM_FOR_LIGHTREC,
-                             sizeof(*next), next);
+                             sizeof(*block_rec), block_rec);
                pthread_cond_signal(&rec->cond);
        }
 
@@ -87,19 +76,21 @@ static void * lightrec_recompiler_thd(void *d)
 
        pthread_mutex_lock(&rec->mutex);
 
-       for (;;) {
+       do {
                do {
                        pthread_cond_wait(&rec->cond, &rec->mutex);
 
-                       if (rec->stop) {
-                               pthread_mutex_unlock(&rec->mutex);
-                               return NULL;
-                       }
+                       if (rec->stop)
+                               goto out_unlock;
 
-               } while (!rec->list);
+               } while (slist_empty(&rec->slist));
 
                lightrec_compile_list(rec);
-       }
+       } while (!rec->stop);
+
+out_unlock:
+       pthread_mutex_unlock(&rec->mutex);
+       return NULL;
 }
 
 struct recompiler *lightrec_recompiler_init(struct lightrec_state *state)
@@ -116,7 +107,7 @@ struct recompiler *lightrec_recompiler_init(struct lightrec_state *state)
        rec->state = state;
        rec->stop = false;
        rec->current_block = NULL;
-       rec->list = NULL;
+       slist_init(&rec->slist);
 
        ret = pthread_cond_init(&rec->cond, NULL);
        if (ret) {
@@ -164,60 +155,77 @@ void lightrec_free_recompiler(struct recompiler *rec)
 
 int lightrec_recompiler_add(struct recompiler *rec, struct block *block)
 {
-       struct block_rec *block_rec, *prev;
+       struct slist_elm *elm, *prev;
+       struct block_rec *block_rec;
+       int ret = 0;
 
        pthread_mutex_lock(&rec->mutex);
 
-       for (block_rec = rec->list, prev = NULL; block_rec;
-            prev = block_rec, block_rec = block_rec->next) {
+       /* 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)
+               goto out_unlock;
+
+       for (elm = slist_first(&rec->slist), prev = NULL; elm;
+            prev = elm, elm = elm->next) {
+               block_rec = container_of(elm, struct block_rec, slist);
+
                if (block_rec->block == block) {
                        /* The block to compile is already in the queue - bump
-                        * it to the top of the list */
-                       if (prev) {
-                               prev->next = block_rec->next;
-                               block_rec->next = rec->list;
-                               rec->list = block_rec;
+                        * it to the top of the list, unless the block is being
+                        * recompiled. */
+                       if (prev && !(block->flags & BLOCK_SHOULD_RECOMPILE)) {
+                               slist_remove_next(prev);
+                               slist_append(&rec->slist, elm);
                        }
 
-                       pthread_mutex_unlock(&rec->mutex);
-                       return 0;
+                       goto out_unlock;
                }
        }
 
        /* By the time this function was called, the block has been recompiled
         * and ins't in the wait list anymore. Just return here. */
-       if (block->function) {
-               pthread_mutex_unlock(&rec->mutex);
-               return 0;
-       }
+       if (block->function && !(block->flags & BLOCK_SHOULD_RECOMPILE))
+               goto out_unlock;
 
        block_rec = lightrec_malloc(rec->state, MEM_FOR_LIGHTREC,
                                    sizeof(*block_rec));
        if (!block_rec) {
-               pthread_mutex_unlock(&rec->mutex);
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto out_unlock;
        }
 
        pr_debug("Adding block PC 0x%x to recompiler\n", block->pc);
 
        block_rec->block = block;
-       block_rec->next = rec->list;
-       rec->list = block_rec;
+
+       elm = &rec->slist;
+
+       /* If the block is being recompiled, push it to the end of the queue;
+        * otherwise push it to the front of the queue. */
+       if (block->flags & BLOCK_SHOULD_RECOMPILE)
+               for (; elm->next; elm = elm->next);
+
+       slist_append(elm, &block_rec->slist);
 
        /* Signal the thread */
        pthread_cond_signal(&rec->cond);
-       pthread_mutex_unlock(&rec->mutex);
 
-       return 0;
+out_unlock:
+       pthread_mutex_unlock(&rec->mutex);
+       return ret;
 }
 
 void lightrec_recompiler_remove(struct recompiler *rec, struct block *block)
 {
        struct block_rec *block_rec;
+       struct slist_elm *elm;
 
        pthread_mutex_lock(&rec->mutex);
 
-       for (block_rec = rec->list; block_rec; block_rec = block_rec->next) {
+       for (elm = slist_first(&rec->slist); elm; elm = elm->next) {
+               block_rec = container_of(elm, struct block_rec, slist);
+
                if (block_rec->block == block) {
                        if (block == rec->current_block) {
                                /* Block is being recompiled - wait for
@@ -229,7 +237,7 @@ void lightrec_recompiler_remove(struct recompiler *rec, struct block *block)
                        } else {
                                /* Block is not yet being processed - remove it
                                 * from the list */
-                               slist_remove(rec, block_rec);
+                               slist_remove(&rec->slist, elm);
                                lightrec_free(rec->state, MEM_FOR_LIGHTREC,
                                              sizeof(*block_rec), block_rec);
                        }
index 99e82aa..999a49f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2019-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
index 956cc3c..8678cc6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
+ * Copyright (C) 2014-2020 Paul Cercueil <paul@crapouillou.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
diff --git a/deps/lightrec/slist.h b/deps/lightrec/slist.h
new file mode 100644 (file)
index 0000000..18195e8
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 Paul Cercueil <paul@crapouillou.net>
+ *
+ * This library 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 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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.
+ */
+
+#ifndef __LIGHTREC_SLIST_H__
+#define __LIGHTREC_SLIST_H__
+
+#define container_of(ptr, type, member)        \
+       ((type *)((void *)(ptr) - offsetof(type, member)))
+
+struct slist_elm {
+       struct slist_elm *next;
+};
+
+static inline void slist_init(struct slist_elm *head)
+{
+       head->next = NULL;
+}
+
+static inline struct slist_elm * slist_first(struct slist_elm *head)
+{
+       return head->next;
+}
+
+static inline _Bool slist_empty(const struct slist_elm *head)
+{
+       return head->next == NULL;
+}
+
+static inline void slist_remove_next(struct slist_elm *elm)
+{
+       if (elm->next)
+               elm->next = elm->next->next;
+}
+
+static inline void slist_remove(struct slist_elm *head, struct slist_elm *elm)
+{
+       struct slist_elm *prev;
+
+       if (head->next == elm) {
+               head->next = elm->next;
+       } else {
+               for (prev = head->next; prev && prev->next != elm; )
+                       prev = prev->next;
+               if (prev)
+                       slist_remove_next(prev);
+       }
+}
+
+static inline void slist_append(struct slist_elm *head, struct slist_elm *elm)
+{
+       elm->next = head->next;
+       head->next = elm;
+}
+
+#endif /* __LIGHTREC_SLIST_H__ */