diff options
Diffstat (limited to 'deps')
-rw-r--r-- | deps/lightrec/.gitrepo | 4 | ||||
-rw-r--r-- | deps/lightrec/CMakeLists.txt | 2 | ||||
-rw-r--r-- | deps/lightrec/README | 1 | ||||
-rw-r--r-- | deps/lightrec/README.md | 53 | ||||
-rw-r--r-- | deps/lightrec/blockcache.c | 8 | ||||
-rw-r--r-- | deps/lightrec/blockcache.h | 5 | ||||
-rw-r--r-- | deps/lightrec/config.h | 1 | ||||
-rw-r--r-- | deps/lightrec/debug.h | 2 | ||||
-rw-r--r-- | deps/lightrec/disassembler.h | 2 | ||||
-rw-r--r-- | deps/lightrec/emitter.c | 5 | ||||
-rw-r--r-- | deps/lightrec/emitter.h | 2 | ||||
-rw-r--r-- | deps/lightrec/interpreter.c | 31 | ||||
-rw-r--r-- | deps/lightrec/interpreter.h | 2 | ||||
-rw-r--r-- | deps/lightrec/lightrec-private.h | 6 | ||||
-rw-r--r-- | deps/lightrec/lightrec.c | 385 | ||||
-rw-r--r-- | deps/lightrec/lightrec.h | 27 | ||||
-rw-r--r-- | deps/lightrec/memmanager.h | 2 | ||||
-rw-r--r-- | deps/lightrec/optimizer.c | 11 | ||||
-rw-r--r-- | deps/lightrec/optimizer.h | 2 | ||||
-rw-r--r-- | deps/lightrec/reaper.c | 123 | ||||
-rw-r--r-- | deps/lightrec/reaper.h | 29 | ||||
-rw-r--r-- | deps/lightrec/recompiler.c | 110 | ||||
-rw-r--r-- | deps/lightrec/recompiler.h | 2 | ||||
-rw-r--r-- | deps/lightrec/regcache.h | 2 | ||||
-rw-r--r-- | deps/lightrec/slist.h | 66 |
25 files changed, 667 insertions, 216 deletions
diff --git a/deps/lightrec/.gitrepo b/deps/lightrec/.gitrepo index 871f638..0dbefe8 100644 --- a/deps/lightrec/.gitrepo +++ b/deps/lightrec/.gitrepo @@ -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 diff --git a/deps/lightrec/CMakeLists.txt b/deps/lightrec/CMakeLists.txt index 6ac5cd4..c58dac5 100644 --- a/deps/lightrec/CMakeLists.txt +++ b/deps/lightrec/CMakeLists.txt @@ -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 index 5bc4627..0000000 --- a/deps/lightrec/README +++ /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 index 0000000..40ecc8f --- /dev/null +++ b/deps/lightrec/README.md @@ -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 diff --git a/deps/lightrec/blockcache.c b/deps/lightrec/blockcache.c index 833a8e1..4263431 100644 --- a/deps/lightrec/blockcache.c +++ b/deps/lightrec/blockcache.c @@ -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; diff --git a/deps/lightrec/blockcache.h b/deps/lightrec/blockcache.h index 0c57ffc..ff63651 100644 --- a/deps/lightrec/blockcache.h +++ b/deps/lightrec/blockcache.h @@ -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__ */ diff --git a/deps/lightrec/config.h b/deps/lightrec/config.h index 64c6a3f..b72ae10 100644 --- a/deps/lightrec/config.h +++ b/deps/lightrec/config.h @@ -21,4 +21,3 @@ #define ENABLE_TINYMM 0 #endif /* __LIGHTREC_CONFIG_H__ */ - diff --git a/deps/lightrec/debug.h b/deps/lightrec/debug.h index 4048d43..4facc22 100644 --- a/deps/lightrec/debug.h +++ b/deps/lightrec/debug.h @@ -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/disassembler.h b/deps/lightrec/disassembler.h index e4c4403..249d094 100644 --- a/deps/lightrec/disassembler.h +++ b/deps/lightrec/disassembler.h @@ -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/emitter.c b/deps/lightrec/emitter.c index b09dc94..206a45c 100644 --- a/deps/lightrec/emitter.c +++ b/deps/lightrec/emitter.c @@ -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] = { diff --git a/deps/lightrec/emitter.h b/deps/lightrec/emitter.h index 57ededf..ec3fc78 100644 --- a/deps/lightrec/emitter.h +++ b/deps/lightrec/emitter.h @@ -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/interpreter.c b/deps/lightrec/interpreter.c index acc41ea..f586685 100644 --- a/deps/lightrec/interpreter.c +++ b/deps/lightrec/interpreter.c @@ -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); } diff --git a/deps/lightrec/interpreter.h b/deps/lightrec/interpreter.h index d4177b3..2113779 100644 --- a/deps/lightrec/interpreter.h +++ b/deps/lightrec/interpreter.h @@ -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 diff --git a/deps/lightrec/lightrec-private.h b/deps/lightrec/lightrec-private.h index 4c9c269..6304515 100644 --- a/deps/lightrec/lightrec-private.h +++ b/deps/lightrec/lightrec-private.h @@ -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; diff --git a/deps/lightrec/lightrec.c b/deps/lightrec/lightrec.c index 47c49c8..7fdf74a 100644 --- a/deps/lightrec/lightrec.c +++ b/deps/lightrec/lightrec.c @@ -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" @@ -43,6 +44,60 @@ 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); diff --git a/deps/lightrec/lightrec.h b/deps/lightrec/lightrec.h index d3d896c..d0793c0 100644 --- a/deps/lightrec/lightrec.h +++ b/deps/lightrec/lightrec.h @@ -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 { diff --git a/deps/lightrec/memmanager.h b/deps/lightrec/memmanager.h index 956e7c7..bd5028d 100644 --- a/deps/lightrec/memmanager.h +++ b/deps/lightrec/memmanager.h @@ -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 diff --git a/deps/lightrec/optimizer.c b/deps/lightrec/optimizer.c index 92b4daa..cf431f2 100644 --- a/deps/lightrec/optimizer.c +++ b/deps/lightrec/optimizer.c @@ -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; diff --git a/deps/lightrec/optimizer.h b/deps/lightrec/optimizer.h index d8def69..84a8fc9 100644 --- a/deps/lightrec/optimizer.h +++ b/deps/lightrec/optimizer.h @@ -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 index 0000000..377685c --- /dev/null +++ b/deps/lightrec/reaper.c @@ -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); +} diff --git a/deps/lightrec/reaper.h b/deps/lightrec/reaper.h new file mode 100644 index 0000000..0309b64 --- /dev/null +++ b/deps/lightrec/reaper.h @@ -0,0 +1,29 @@ +/* + * 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_REAPER_H__ +#define __LIGHTREC_REAPER_H__ + +struct lightrec_state; +struct reaper; + +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__ */ diff --git a/deps/lightrec/recompiler.c b/deps/lightrec/recompiler.c index 379881a..e60889c 100644 --- a/deps/lightrec/recompiler.c +++ b/deps/lightrec/recompiler.c @@ -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); } diff --git a/deps/lightrec/recompiler.h b/deps/lightrec/recompiler.h index 99e82aa..999a49f 100644 --- a/deps/lightrec/recompiler.h +++ b/deps/lightrec/recompiler.h @@ -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 diff --git a/deps/lightrec/regcache.h b/deps/lightrec/regcache.h index 956cc3c..8678cc6 100644 --- a/deps/lightrec/regcache.h +++ b/deps/lightrec/regcache.h @@ -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 index 0000000..18195e8 --- /dev/null +++ b/deps/lightrec/slist.h @@ -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__ */ |