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, u32 data)
{
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;
if (has_code_buffer) {
jit_get_code(&code_size);
+
+#ifdef __i386__
+ /* Lightning's code size estimation routine is buggy on x86 and
+ * will return a value that's too small. */
+ code_size *= 2;
+#endif
+
code = lightrec_alloc_code(state, (size_t) code_size);
if (!code) {
}
code = jit_emit();
+ if (!code) {
+ if (has_code_buffer)
+ lightrec_free_code(state, code);
+
+ return NULL;
+ }
jit_get_code(&new_code_size);
lightrec_register(MEM_FOR_CODE, new_code_size);
jit_node_t *to_end[C_WRAPPERS_COUNT - 1];
u8 tmp = JIT_R1;
-#ifdef __sh__
- /* On SH, GBR-relative loads target the r0 register.
- * Use it as the temporary register to factorize the move to
- * JIT_R1. */
- if (LIGHTREC_REG_STATE == _GBR)
- tmp = _R0;
-#endif
-
block = lightrec_malloc(state, MEM_FOR_IR, sizeof(*block));
if (!block)
goto err_no_mem;
block->function = lightrec_emit_code(state, block, _jit,
&block->code_size);
if (!block->function)
- goto err_free_block;
+ goto err_free_jit;
state->wrappers_eps[C_WRAPPERS_COUNT - 1] = block->function;
jit_clear_state();
return block;
+err_free_jit:
+ jit_destroy_state();
err_free_block:
lightrec_free(state, MEM_FOR_IR, sizeof(*block), block);
err_no_mem:
to_end = jit_blei(LIGHTREC_REG_CYCLE, 0);
/* Convert next PC to KUNSEG and avoid mirrors */
- jit_andi(JIT_V1, JIT_V0, 0x10000000 | (RAM_SIZE - 1));
- jit_rshi_u(JIT_R1, JIT_V1, 28);
+ jit_andi(JIT_V1, JIT_V0, RAM_SIZE - 1);
jit_andi(JIT_R2, JIT_V0, BIOS_SIZE - 1);
+ jit_andi(JIT_R1, JIT_V0, BIT(28));
jit_addi(JIT_R2, JIT_R2, RAM_SIZE);
jit_movnr(JIT_V1, JIT_R2, JIT_R1);
block->function = lightrec_emit_code(state, block, _jit,
&block->code_size);
if (!block->function)
- goto err_free_block;
+ goto err_free_jit;
state->eob_wrapper_func = jit_address(addr2);
if (OPT_DETECT_IMPOSSIBLE_BRANCHES)
jit_clear_state();
return block;
+err_free_jit:
+ jit_destroy_state();
err_free_block:
lightrec_free(state, MEM_FOR_IR, sizeof(*block), block);
err_no_mem:
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;
/* 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)
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
/* 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
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);
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);
}
}
+ 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();
}
struct lightrec_state * lightrec_init(char *argv0,
- const struct lightrec_mem_map *map,
+ const struct lightrec_mem_map *maps,
size_t nb,
const struct lightrec_ops *ops)
{
- const struct lightrec_mem_map *codebuf_map = &map[PSX_MAP_CODE_BUFFER];
+ const struct lightrec_mem_map *codebuf_map = &maps[PSX_MAP_CODE_BUFFER];
+ const struct lightrec_mem_map *map;
struct lightrec_state *state;
uintptr_t addr;
void *tlsf = NULL;
}
state->nb_maps = nb;
- state->maps = map;
+ state->maps = maps;
memcpy(&state->ops, ops, sizeof(*ops));
state->c_wrappers[C_WRAPPER_MTC] = lightrec_mtc_cb;
state->c_wrappers[C_WRAPPER_CP] = lightrec_cp_cb;
- map = &state->maps[PSX_MAP_BIOS];
+ map = &maps[PSX_MAP_BIOS];
state->offset_bios = (uintptr_t)map->address - map->pc;
- map = &state->maps[PSX_MAP_SCRATCH_PAD];
+ map = &maps[PSX_MAP_SCRATCH_PAD];
state->offset_scratch = (uintptr_t)map->address - map->pc;
- map = &state->maps[PSX_MAP_HW_REGISTERS];
+ map = &maps[PSX_MAP_HW_REGISTERS];
state->offset_io = (uintptr_t)map->address - map->pc;
- map = &state->maps[PSX_MAP_KERNEL_USER_RAM];
+ map = &maps[PSX_MAP_KERNEL_USER_RAM];
state->offset_ram = (uintptr_t)map->address - map->pc;
- if (state->maps[PSX_MAP_MIRROR1].address == map->address + 0x200000 &&
- state->maps[PSX_MAP_MIRROR2].address == map->address + 0x400000 &&
- state->maps[PSX_MAP_MIRROR3].address == map->address + 0x600000)
+ if (maps[PSX_MAP_MIRROR1].address == map->address + 0x200000 &&
+ maps[PSX_MAP_MIRROR2].address == map->address + 0x400000 &&
+ maps[PSX_MAP_MIRROR3].address == map->address + 0x600000)
state->mirrors_mapped = true;
if (state->offset_bios == 0 &&
void lightrec_set_cycles_per_opcode(struct lightrec_state *state, u32 cycles)
{
+ if (state->cycles_per_op == cycles)
+ return;
+
state->cycles_per_op = cycles;
+
+ if (ENABLE_THREADED_COMPILER) {
+ lightrec_recompiler_pause(state->rec);
+ lightrec_reaper_reap(state->reaper);
+ }
+
+ lightrec_invalidate_all(state);
+ lightrec_free_all_blocks(state->block_cache);
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_recompiler_unpause(state->rec);
}