git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / lightrec / lightrec.c
index 79db447..1cfc427 100644 (file)
@@ -35,19 +35,21 @@ static bool lightrec_block_is_fully_tagged(const struct block *block);
 static void lightrec_mtc2(struct lightrec_state *state, u8 reg, u32 data);
 static u32 lightrec_mfc2(struct lightrec_state *state, u8 reg);
 
+static void lightrec_reap_block(struct lightrec_state *state, void *data);
+
 static void lightrec_default_sb(struct lightrec_state *state, u32 opcode,
-                               void *host, u32 addr, u8 data)
+                               void *host, u32 addr, u32 data)
 {
-       *(u8 *)host = data;
+       *(u8 *)host = (u8)data;
 
        if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
                lightrec_invalidate(state, addr, 1);
 }
 
 static void lightrec_default_sh(struct lightrec_state *state, u32 opcode,
-                               void *host, u32 addr, u16 data)
+                               void *host, u32 addr, u32 data)
 {
-       *(u16 *)host = HTOLE16(data);
+       *(u16 *)host = HTOLE16((u16)data);
 
        if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
                lightrec_invalidate(state, addr, 2);
@@ -80,6 +82,27 @@ static u32 lightrec_default_lw(struct lightrec_state *state,
        return LE32TOH(*(u32 *)host);
 }
 
+static u32 lightrec_default_lwu(struct lightrec_state *state,
+                               u32 opcode, void *host, u32 addr)
+{
+       u32 val;
+
+       memcpy(&val, host, 4);
+
+       return LE32TOH(val);
+}
+
+static void lightrec_default_swu(struct lightrec_state *state, u32 opcode,
+                                void *host, u32 addr, u32 data)
+{
+       data = HTOLE32(data);
+
+       memcpy(host, &data, 4);
+
+       if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
+               lightrec_invalidate(state, addr & ~0x3, 8);
+}
+
 static const struct lightrec_mem_map_ops lightrec_default_ops = {
        .sb = lightrec_default_sb,
        .sh = lightrec_default_sh,
@@ -87,6 +110,8 @@ static const struct lightrec_mem_map_ops lightrec_default_ops = {
        .lb = lightrec_default_lb,
        .lh = lightrec_default_lh,
        .lw = lightrec_default_lw,
+       .lwu = lightrec_default_lwu,
+       .swu = lightrec_default_swu,
 };
 
 static void __segfault_cb(struct lightrec_state *state, u32 addr,
@@ -94,9 +119,9 @@ static void __segfault_cb(struct lightrec_state *state, u32 addr,
 {
        lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
        pr_err("Segmentation fault in recompiled code: invalid "
-              "load/store at address 0x%08x\n", addr);
+              "load/store at address "PC_FMT"\n", addr);
        if (block)
-               pr_err("Was executing block PC 0x%08x\n", block->pc);
+               pr_err("Was executing block "PC_FMT"\n", block->pc);
 }
 
 static void lightrec_swl(struct lightrec_state *state,
@@ -290,7 +315,7 @@ u32 lightrec_rw(struct lightrec_state *state, union code op, u32 base,
                old_flags = block_set_flags(block, BLOCK_SHOULD_RECOMPILE);
 
                if (!(old_flags & BLOCK_SHOULD_RECOMPILE)) {
-                       pr_debug("Opcode of block at PC 0x%08x has been tagged"
+                       pr_debug("Opcode of block at "PC_FMT" has been tagged"
                                 " - flag for recompilation\n", block->pc);
 
                        lut_write(state, lut_offset(block->pc), NULL);
@@ -299,10 +324,10 @@ u32 lightrec_rw(struct lightrec_state *state, union code op, u32 base,
 
        switch (op.i.op) {
        case OP_SB:
-               ops->sb(state, opcode, host, addr, (u8) data);
+               ops->sb(state, opcode, host, addr, data);
                return 0;
        case OP_SH:
-               ops->sh(state, opcode, host, addr, (u16) data);
+               ops->sh(state, opcode, host, addr, data);
                return 0;
        case OP_SWL:
                lightrec_swl(state, ops, opcode, host, addr, data);
@@ -331,6 +356,11 @@ u32 lightrec_rw(struct lightrec_state *state, union code op, u32 base,
                return lightrec_lwl(state, ops, opcode, host, addr, data);
        case OP_LWR:
                return lightrec_lwr(state, ops, opcode, host, addr, data);
+       case OP_META_LWU:
+               return ops->lwu(state, opcode, host, addr);
+       case OP_META_SWU:
+               ops->swu(state, opcode, host, addr, data);
+               return 0;
        case OP_LW:
        default:
                return ops->lw(state, opcode, host, addr);
@@ -352,6 +382,7 @@ static void lightrec_rw_helper(struct lightrec_state *state,
        case OP_LWL:
        case OP_LWR:
        case OP_LW:
+       case OP_META_LWU:
                if (OPT_HANDLE_LOAD_DELAYS && unlikely(!state->in_delay_slot_n)) {
                        state->temp_reg = ret;
                        state->in_delay_slot_n = 0xff;
@@ -378,7 +409,7 @@ static void lightrec_rw_generic_cb(struct lightrec_state *state, u32 arg)
        block = lightrec_find_block_from_lut(state->block_cache,
                                             arg >> 16, state->curr_pc);
        if (unlikely(!block)) {
-               pr_err("rw_generic: No block found in LUT for PC 0x%x offset 0x%x\n",
+               pr_err("rw_generic: No block found in LUT for "PC_FMT" offset 0x%"PRIx16"\n",
                         state->curr_pc, offset);
                lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
                return;
@@ -665,7 +696,7 @@ static struct block * lightrec_get_block(struct lightrec_state *state, u32 pc)
        u8 old_flags;
 
        if (block && lightrec_block_is_outdated(state, block)) {
-               pr_debug("Block at PC 0x%08x is outdated!\n", block->pc);
+               pr_debug("Block at "PC_FMT" is outdated!\n", block->pc);
 
                old_flags = block_set_flags(block, BLOCK_IS_DEAD);
                if (!(old_flags & BLOCK_IS_DEAD)) {
@@ -674,9 +705,15 @@ static struct block * lightrec_get_block(struct lightrec_state *state, u32 pc)
                        if (ENABLE_THREADED_COMPILER)
                                lightrec_recompiler_remove(state->rec, block);
 
-                       lightrec_unregister_block(state->block_cache, block);
                        remove_from_code_lut(state->block_cache, block);
-                       lightrec_free_block(state, block);
+
+                       if (ENABLE_THREADED_COMPILER) {
+                               lightrec_reaper_add(state->reaper,
+                                                   lightrec_reap_block, block);
+                       } else {
+                               lightrec_unregister_block(state->block_cache, block);
+                               lightrec_free_block(state, block);
+                       }
                }
 
                block = NULL;
@@ -685,7 +722,7 @@ static struct block * lightrec_get_block(struct lightrec_state *state, u32 pc)
        if (!block) {
                block = lightrec_precompile_block(state, pc);
                if (!block) {
-                       pr_err("Unable to recompile block at PC 0x%x\n", pc);
+                       pr_err("Unable to recompile block at "PC_FMT"\n", pc);
                        lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
                        return NULL;
                }
@@ -724,7 +761,7 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
                        !block_has_flag(block, BLOCK_IS_DEAD);
 
                if (unlikely(should_recompile)) {
-                       pr_debug("Block at PC 0x%08x should recompile\n", pc);
+                       pr_debug("Block at "PC_FMT" should recompile\n", pc);
 
                        if (ENABLE_THREADED_COMPILER) {
                                lightrec_recompiler_add(state->rec, block);
@@ -1018,12 +1055,12 @@ static u32 lightrec_memset(struct lightrec_state *state)
        u32 length = state->regs.gpr[5] * 4;
 
        if (!map) {
-               pr_err("Unable to find memory map for memset target address "
-                      "0x%x\n", kunseg_pc);
+               pr_err("Unable to find memory map for memset target address "PC_FMT"\n",
+                      kunseg_pc);
                return 0;
        }
 
-       pr_debug("Calling host memset, PC 0x%x (host address 0x%" PRIxPTR ") for %u bytes\n",
+       pr_debug("Calling host memset, "PC_FMT" (host address 0x%"PRIxPTR") for %u bytes\n",
                 kunseg_pc, (uintptr_t)host, length);
        memset(host, 0, length);
 
@@ -1046,7 +1083,7 @@ static u32 lightrec_check_load_delay(struct lightrec_state *state, u32 pc, u8 re
        } else {
                block = lightrec_get_block(state, pc);
                if (unlikely(!block)) {
-                       pr_err("Unable to get block at PC 0x%08x\n", pc);
+                       pr_err("Unable to get block at "PC_FMT"\n", pc);
                        lightrec_set_exit_flags(state, LIGHTREC_EXIT_SEGFAULT);
                        pc = 0;
                } else {
@@ -1482,6 +1519,8 @@ static bool lightrec_block_is_fully_tagged(const struct block *block)
                case OP_SWR:
                case OP_LWC2:
                case OP_SWC2:
+               case OP_META_LWU:
+               case OP_META_SWU:
                        if (!LIGHTREC_FLAGS_GET_IO_MODE(op->flags))
                                return false;
                        fallthrough;
@@ -1497,7 +1536,7 @@ static void lightrec_reap_block(struct lightrec_state *state, void *data)
 {
        struct block *block = data;
 
-       pr_debug("Reap dead block at PC 0x%08x\n", block->pc);
+       pr_debug("Reap dead block at "PC_FMT"\n", block->pc);
        lightrec_unregister_block(state->block_cache, block);
        lightrec_free_block(state, block);
 }
@@ -1528,6 +1567,8 @@ static void lightrec_reap_opcode_list(struct lightrec_state *state, void *data)
 int lightrec_compile_block(struct lightrec_cstate *cstate,
                           struct block *block)
 {
+       struct block *dead_blocks[ARRAY_SIZE(cstate->targets)];
+       u32 was_dead[ARRAY_SIZE(cstate->targets) / 8];
        struct lightrec_state *state = cstate->state;
        struct lightrec_branch_target *target;
        bool fully_tagged = false;
@@ -1646,11 +1687,8 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
        /* Add compiled function to the LUT */
        lut_write(state, lut_offset(block->pc), block->function);
 
-       if (ENABLE_THREADED_COMPILER)
-               lightrec_reaper_continue(state->reaper);
-
        /* Detect old blocks that have been covered by the new one */
-       for (i = 0; i < cstate->nb_targets; i++) {
+       for (i = 0; ENABLE_THREADED_COMPILER && i < cstate->nb_targets; i++) {
                target = &cstate->targets[i];
 
                if (!target->offset)
@@ -1658,12 +1696,6 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
 
                offset = block->pc + target->offset * sizeof(u32);
 
-               /* Pause the reaper while we search for the block until we set
-                * the BLOCK_IS_DEAD flag, otherwise the block may be removed
-                * under our feet. */
-               if (ENABLE_THREADED_COMPILER)
-                       lightrec_reaper_pause(state->reaper);
-
                block2 = lightrec_find_block(state->block_cache, offset);
                if (block2) {
                        /* No need to check if block2 is compilable - it must
@@ -1672,17 +1704,26 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
                        /* Set the "block dead" flag to prevent the dynarec from
                         * recompiling this block */
                        old_flags = block_set_flags(block2, BLOCK_IS_DEAD);
+
+                       if (old_flags & BLOCK_IS_DEAD)
+                               was_dead[i / 32] |= BIT(i % 32);
+                       else
+                               was_dead[i / 32] &= ~BIT(i % 32);
                }
 
-               if (ENABLE_THREADED_COMPILER) {
-                       lightrec_reaper_continue(state->reaper);
+               dead_blocks[i] = block2;
 
-                       /* If block2 was pending for compilation, cancel it.
-                        * If it's being compiled right now, wait until it
-                        * finishes. */
-                       if (block2)
-                               lightrec_recompiler_remove(state->rec, block2);
-               }
+               /* If block2 was pending for compilation, cancel it.
+                * If it's being compiled right now, wait until it finishes. */
+               if (block2)
+                       lightrec_recompiler_remove(state->rec, block2);
+       }
+
+       for (i = 0; i < cstate->nb_targets; i++) {
+               target = &cstate->targets[i];
+
+               if (!target->offset)
+                       continue;
 
                /* We know from now on that block2 (if present) isn't going to
                 * be compiled. We can override the LUT entry with our new
@@ -1690,6 +1731,12 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
                offset = lut_offset(block->pc) + target->offset;
                lut_write(state, offset, jit_address(target->label));
 
+               if (ENABLE_THREADED_COMPILER) {
+                       block2 = dead_blocks[i];
+               } else {
+                       offset = block->pc + target->offset * sizeof(u32);
+                       block2 = lightrec_find_block(state->block_cache, offset);
+               }
                if (block2) {
                        pr_debug("Reap block 0x%08x as it's covered by block "
                                 "0x%08x\n", block2->pc, block->pc);
@@ -1698,7 +1745,7 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
                        if (!ENABLE_THREADED_COMPILER) {
                                lightrec_unregister_block(state->block_cache, block2);
                                lightrec_free_block(state, block2);
-                       } else if (!(old_flags & BLOCK_IS_DEAD)) {
+                       } else if (!(was_dead[i / 32] & BIT(i % 32))) {
                                lightrec_reaper_add(state->reaper,
                                                    lightrec_reap_block,
                                                    block2);
@@ -1706,6 +1753,9 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
                }
        }
 
+       if (ENABLE_THREADED_COMPILER)
+               lightrec_reaper_continue(state->reaper);
+
        if (ENABLE_DISASSEMBLER) {
                pr_debug("Compiling block at PC: 0x%08x\n", block->pc);
                jit_disassemble();
@@ -1717,7 +1767,7 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
                old_flags = block_set_flags(block, BLOCK_NO_OPCODE_LIST);
 
        if (fully_tagged && !(old_flags & BLOCK_NO_OPCODE_LIST)) {
-               pr_debug("Block PC 0x%08x is fully tagged"
+               pr_debug("Block "PC_FMT" is fully tagged"
                         " - free opcode list\n", block->pc);
 
                if (ENABLE_THREADED_COMPILER) {
@@ -1910,7 +1960,7 @@ struct lightrec_state * lightrec_init(char *argv0,
        else
                lut_size = CODE_LUT_SIZE * sizeof(void *);
 
-       init_jit(argv0);
+       init_jit_with_debug(argv0, stdout);
 
        state = calloc(1, sizeof(*state) + lut_size);
        if (!state)