git subrepo pull --force deps/lightrec
[pcsx_rearmed.git] / deps / lightrec / lightrec.c
index d5b1de9..13434b4 100644 (file)
@@ -36,20 +36,20 @@ 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_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->invalidate_from_dma_only)
+       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->invalidate_from_dma_only)
+       if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
                lightrec_invalidate(state, addr, 2);
 }
 
@@ -58,7 +58,7 @@ static void lightrec_default_sw(struct lightrec_state *state, u32 opcode,
 {
        *(u32 *)host = HTOLE32(data);
 
-       if (!state->invalidate_from_dma_only)
+       if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
                lightrec_invalidate(state, addr, 4);
 }
 
@@ -80,6 +80,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 +108,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 +117,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 +313,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 +322,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 +354,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 +380,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;
@@ -376,10 +405,10 @@ static void lightrec_rw_generic_cb(struct lightrec_state *state, u32 arg)
        u16 offset = (u16)arg;
 
        block = lightrec_find_block_from_lut(state->block_cache,
-                                            arg >> 16, state->next_pc);
+                                            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",
-                        state->next_pc, offset);
+               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;
        }
@@ -524,7 +553,7 @@ static void lightrec_mtc0(struct lightrec_state *state, u8 reg, u32 data)
                status = state->regs.cp0[12];
 
                /* Handle software interrupts */
-               if (!!(status & cause & 0x300) & status)
+               if ((!!(status & cause & 0x300)) & status)
                        lightrec_set_exit_flags(state, LIGHTREC_EXIT_CHECK_INTERRUPT);
 
                /* Handle hardware interrupts */
@@ -665,7 +694,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)) {
@@ -685,7 +714,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 +753,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);
@@ -776,7 +805,7 @@ static void * get_next_block_func(struct lightrec_state *state, u32 pc)
        } while (state->exit_flags == LIGHTREC_EXIT_NORMAL
                 && state->current_cycle < state->target_cycle);
 
-       state->next_pc = pc;
+       state->curr_pc = pc;
        return func;
 }
 
@@ -1018,16 +1047,16 @@ 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);
 
-       if (!state->invalidate_from_dma_only)
+       if (!(state->opt_flags & LIGHTREC_OPT_INV_DMA_ONLY))
                lightrec_invalidate_map(state, map, kunseg_pc, length);
 
        /* Rough estimation of the number of cycles consumed */
@@ -1046,7 +1075,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 {
@@ -1077,6 +1106,14 @@ static void update_cycle_counter_after_c(jit_state_t *_jit)
        jit_subr(LIGHTREC_REG_CYCLE, JIT_R2, JIT_R1);
 }
 
+static void sync_next_pc(jit_state_t *_jit)
+{
+       if (lightrec_store_next_pc()) {
+               jit_ldxi_ui(JIT_V0, LIGHTREC_REG_STATE,
+                           offsetof(struct lightrec_state, next_pc));
+       }
+}
+
 static struct block * generate_dispatcher(struct lightrec_state *state)
 {
        struct block *block;
@@ -1140,6 +1177,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
                 * in JIT_V0 and the address of the block in JIT_V1. */
                addr4 = jit_indirect();
 
+               sync_next_pc(_jit);
                update_cycle_counter_before_c(_jit);
 
                jit_prepare();
@@ -1167,6 +1205,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
                 * in question; and if it does, handle it accordingly. */
                addr5 = jit_indirect();
 
+               sync_next_pc(_jit);
                update_cycle_counter_before_c(_jit);
 
                jit_prepare();
@@ -1178,22 +1217,24 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
                jit_retval(JIT_V0);
 
                update_cycle_counter_after_c(_jit);
-
-               if (OPT_DETECT_IMPOSSIBLE_BRANCHES)
-                       jit_patch(jmp2);
        }
 
+       /* The block will jump here, with the number of cycles remaining in
+        * LIGHTREC_REG_CYCLE */
+       addr2 = jit_indirect();
+
+       sync_next_pc(_jit);
+
+       if (OPT_HANDLE_LOAD_DELAYS && OPT_DETECT_IMPOSSIBLE_BRANCHES)
+             jit_patch(jmp2);
+
        if (OPT_REPLACE_MEMSET
            && (OPT_DETECT_IMPOSSIBLE_BRANCHES || OPT_HANDLE_LOAD_DELAYS)) {
                jit_patch(jmp);
        }
 
-       /* The block will jump here, with the number of cycles remaining in
-        * LIGHTREC_REG_CYCLE */
-       addr2 = jit_indirect();
-
-       /* Store back the next_pc to the lightrec_state structure */
-       offset = offsetof(struct lightrec_state, next_pc);
+       /* Store back the next PC to the lightrec_state structure */
+       offset = offsetof(struct lightrec_state, curr_pc);
        jit_stxi_i(offset, LIGHTREC_REG_STATE, JIT_V0);
 
        /* Jump to end if state->target_cycle < state->current_cycle */
@@ -1254,7 +1295,7 @@ static struct block * generate_dispatcher(struct lightrec_state *state)
 
        /* Reset JIT_V0 to the next PC */
        jit_ldxi_ui(JIT_V0, LIGHTREC_REG_STATE,
-                   offsetof(struct lightrec_state, next_pc));
+                   offsetof(struct lightrec_state, curr_pc));
 
        /* If we get non-NULL, loop */
        jit_patch_at(jit_bnei(JIT_V1, 0), loop);
@@ -1311,9 +1352,10 @@ union code lightrec_read_opcode(struct lightrec_state *state, u32 pc)
        return (union code) LE32TOH(*code);
 }
 
-__cnst unsigned int lightrec_cycles_of_opcode(union code code)
+unsigned int lightrec_cycles_of_opcode(const struct lightrec_state *state,
+                                      union code code)
 {
-       return 2;
+       return state->cycles_per_op;
 }
 
 void lightrec_free_opcode_list(struct lightrec_state *state, struct opcode *ops)
@@ -1469,6 +1511,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;
@@ -1484,7 +1528,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);
 }
@@ -1543,7 +1587,9 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
        block->_jit = _jit;
 
        lightrec_regcache_reset(cstate->reg_cache);
-       lightrec_preload_pc(cstate->reg_cache);
+
+       if (OPT_PRELOAD_PC && (block->flags & BLOCK_PRELOAD_PC))
+               lightrec_preload_pc(cstate->reg_cache, _jit);
 
        cstate->cycles = 0;
        cstate->nb_local_branches = 0;
@@ -1581,7 +1627,7 @@ int lightrec_compile_block(struct lightrec_cstate *cstate,
 #endif
                }
 
-               cstate->cycles += lightrec_cycles_of_opcode(elm->c);
+               cstate->cycles += lightrec_cycles_of_opcode(state, elm->c);
        }
 
        for (i = 0; i < cstate->nb_local_branches; i++) {
@@ -1702,7 +1748,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) {
@@ -1763,13 +1809,13 @@ u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
                target_cycle = UINT_MAX;
 
        state->target_cycle = target_cycle;
-       state->next_pc = pc;
+       state->curr_pc = pc;
 
        block_trace = get_next_block_func(state, pc);
        if (block_trace) {
                cycles_delta = state->target_cycle - state->current_cycle;
 
-               cycles_delta = (*func)(state, state->next_pc,
+               cycles_delta = (*func)(state, state->curr_pc,
                                       block_trace, cycles_delta);
 
                state->current_cycle = state->target_cycle - cycles_delta;
@@ -1781,7 +1827,7 @@ u32 lightrec_execute(struct lightrec_state *state, u32 pc, u32 target_cycle)
        if (LOG_LEVEL >= INFO_L)
                lightrec_print_info(state);
 
-       return state->next_pc;
+       return state->curr_pc;
 }
 
 u32 lightrec_run_interpreter(struct lightrec_state *state, u32 pc,
@@ -1895,7 +1941,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)
@@ -1906,6 +1952,7 @@ struct lightrec_state * lightrec_init(char *argv0,
        state->tlsf = tlsf;
        state->with_32bit_lut = with_32bit_lut;
        state->in_delay_slot_n = 0xff;
+       state->cycles_per_op = 2;
 
        state->block_cache = lightrec_blockcache_init(state);
        if (!state->block_cache)
@@ -2052,12 +2099,12 @@ void lightrec_invalidate_all(struct lightrec_state *state)
        memset(state->code_lut, 0, lut_elm_size(state) * CODE_LUT_SIZE);
 }
 
-void lightrec_set_invalidate_mode(struct lightrec_state *state, bool dma_only)
+void lightrec_set_unsafe_opt_flags(struct lightrec_state *state, u32 flags)
 {
-       if (state->invalidate_from_dma_only != dma_only)
+       if ((flags ^ state->opt_flags) & LIGHTREC_OPT_INV_DMA_ONLY)
                lightrec_invalidate_all(state);
 
-       state->invalidate_from_dma_only = dma_only;
+       state->opt_flags = flags;
 }
 
 void lightrec_set_exit_flags(struct lightrec_state *state, u32 flags)
@@ -2100,3 +2147,8 @@ struct lightrec_registers * lightrec_get_registers(struct lightrec_state *state)
 {
        return &state->regs;
 }
+
+void lightrec_set_cycles_per_opcode(struct lightrec_state *state, u32 cycles)
+{
+       state->cycles_per_op = cycles;
+}