[subrepo]
remote = https://github.com/pcercuei/lightrec.git
branch = master
- commit = 49ef275a66aad8540ab73b09b0dd2128ebe4d6dc
- parent = a0467ff492a25521867fcfb7d66b9c617017151a
+ commit = a8fe7568cef718517b230a13fe48891faa60f04b
+ parent = 2f609094ad9ec14212fb730897ccb63f2f44bc40
method = merge
cmdver = 0.4.3
target_compile_options(${PROJECT_NAME} PRIVATE -Wno-initializer-overrides)
endif()
-option(ENABLE_TINYMM "Enable optional libtinymm dependency" OFF)
-if (ENABLE_TINYMM)
- find_library(TINYMM_LIBRARIES tinymm REQUIRED)
- find_path(TINYMM_INCLUDE_DIR tinymm.h REQUIRED)
-
- include_directories(${TINYMM_INCLUDE_DIR})
- target_link_libraries(${PROJECT_NAME} PRIVATE ${TINYMM_LIBRARIES})
-endif (ENABLE_TINYMM)
-
if (ENABLE_THREADED_COMPILER)
find_library(PTHREAD_LIBRARIES pthread REQUIRED)
find_path(PTHREAD_INCLUDE_DIR pthread.h REQUIRED)
target_include_directories(${PROJECT_NAME} PRIVATE tlsf)
endif (ENABLE_CODE_BUFFER)
-if (ENABLE_CODE_BUFFER AND ENABLE_THREADED_COMPILER)
- message(SEND_ERROR "External code buffer cannot be used along with the threaded compiler")
-endif (ENABLE_CODE_BUFFER AND ENABLE_THREADED_COMPILER)
-
find_library(LIBLIGHTNING lightning REQUIRED)
find_path(LIBLIGHTNING_INCLUDE_DIR lightning.h REQUIRED)
pr_err("Block at PC 0x%x is not in cache\n", block->pc);
}
-void lightrec_free_block_cache(struct blockcache *cache)
+static bool lightrec_block_is_old(const struct lightrec_state *state,
+ const struct block *block)
+{
+ u32 diff = state->current_cycle - block->precompile_date;
+
+ return diff > (1 << 27); /* About 4 seconds */
+}
+
+static void lightrec_free_blocks(struct blockcache *cache,
+ const struct block *except, bool all)
{
+ struct lightrec_state *state = cache->state;
struct block *block, *next;
+ bool outdated = all;
unsigned int i;
for (i = 0; i < LUT_SIZE; i++) {
for (block = cache->lut[i]; block; block = next) {
next = block->next;
- lightrec_free_block(cache->state, block);
+
+ if (except && block == except)
+ continue;
+
+ if (!all) {
+ outdated = lightrec_block_is_old(state, block) ||
+ lightrec_block_is_outdated(state, block);
+ }
+
+ if (outdated) {
+ pr_debug("Freeing outdated block at PC 0x%08x\n", block->pc);
+ remove_from_code_lut(cache, block);
+ lightrec_unregister_block(cache, block);
+ lightrec_free_block(state, block);
+ }
}
}
+}
+
+void lightrec_remove_outdated_blocks(struct blockcache *cache,
+ const struct block *except)
+{
+ pr_info("Running out of code space. Cleaning block cache...\n");
+ lightrec_free_blocks(cache, except, false);
+}
+
+void lightrec_free_block_cache(struct blockcache *cache)
+{
+ lightrec_free_blocks(cache, NULL, true);
lightrec_free(cache->state, MEM_FOR_LIGHTREC, sizeof(*cache), cache);
}
u32 lightrec_calculate_block_hash(const struct block *block);
_Bool lightrec_block_is_outdated(struct lightrec_state *state, struct block *block);
+void lightrec_remove_outdated_blocks(struct blockcache *cache,
+ const struct block *except);
+
#endif /* __BLOCKCACHE_H__ */
lightrec_reg_name(c.r.rt),
lightrec_reg_name(c.r.rs));
case OP_SPECIAL_JR:
+ *flags_ptr = opcode_branch_flags;
+ *nb_flags = ARRAY_SIZE(opcode_branch_flags);
+ fallthrough;
case OP_SPECIAL_MTHI:
case OP_SPECIAL_MTLO:
return snprintf(buf, len, "%s%s",
pc + 4 + ((s16)c.i.imm << 2));
case OP_J:
case OP_JAL:
+ *flags_ptr = opcode_branch_flags;
+ *nb_flags = ARRAY_SIZE(opcode_branch_flags);
return snprintf(buf, len, "%s0x%x",
std_opcodes[c.i.op],
(pc & 0xf0000000) | (c.j.imm << 2));
#cmakedefine01 ENABLE_THREADED_COMPILER
#cmakedefine01 ENABLE_FIRST_PASS
#cmakedefine01 ENABLE_DISASSEMBLER
-#cmakedefine01 ENABLE_TINYMM
#cmakedefine01 ENABLE_CODE_BUFFER
#cmakedefine01 HAS_DEFAULT_ELM
#define SET_DEFAULT_ELM(table, value) [0] = NULL
#endif
+#define fallthrough do {} while (0) /* fall-through */
+
/* Flags for (struct block *)->flags */
#define BLOCK_NEVER_COMPILE BIT(0)
#define BLOCK_SHOULD_RECOMPILE BIT(1)
struct recompiler;
struct regcache;
struct opcode;
-struct tinymm;
struct reaper;
struct block {
struct block *next;
u32 pc;
u32 hash;
+ u32 precompile_date;
unsigned int code_size;
u16 nb_ops;
u8 flags;
struct block *dispatcher, *c_wrapper_block;
void *c_wrappers[C_WRAPPERS_COUNT];
void *wrappers_eps[C_WRAPPERS_COUNT];
- struct tinymm *tinymm;
struct blockcache *block_cache;
struct recompiler *rec;
struct lightrec_cstate *cstate;
static inline void * lut_read(struct lightrec_state *state, u32 offset)
{
- void **lut_entry = lut_address(state, lut_offset(offset));
+ void **lut_entry = lut_address(state, offset);
if (lut_is_32bit(state))
return (void *)(uintptr_t) *(u32 *) lut_entry;
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
-#if ENABLE_TINYMM
-#include <tinymm.h>
-#endif
#define GENMASK(h, l) \
(((uintptr_t)-1 << (l)) & ((uintptr_t)-1 >> (__WORDSIZE - 1 - (h))))
case OP_LW:
if (op.i.rt)
state->regs.gpr[op.i.rt] = ret;
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
}
clamp_s32(gteir3 >> 7, 0, 0x1f) << 10;
case 15:
reg = 14;
- default: /* fall-through */
+ fallthrough;
+ default:
return state->regs.cp2d[reg];
}
}
return;
case 30:
state->regs.cp2d[31] = count_leading_bits((s32) data);
- default: /* fall-through */
+ fallthrough;
+ default:
state->regs.cp2d[reg] = data;
break;
}
break;
case 31:
data = (data & 0x7ffff000) | !!(data & 0x7f87e000) << 31;
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
struct block *block;
bool should_recompile;
void *func;
+ int err;
for (;;) {
- func = lut_read(state, pc);
+ func = lut_read(state, lut_offset(pc));
if (func && func != state->get_next_block)
break;
lightrec_unregister(MEM_FOR_CODE, block->code_size);
- if (ENABLE_THREADED_COMPILER)
+ if (ENABLE_THREADED_COMPILER) {
lightrec_recompiler_add(state->rec, block);
- else
- lightrec_compile_block(state->cstate, block);
+ } else {
+ err = lightrec_compile_block(state->cstate, block);
+ if (err) {
+ state->exit_flags = LIGHTREC_EXIT_NOMEM;
+ return NULL;
+ }
+ }
}
if (ENABLE_THREADED_COMPILER && likely(!should_recompile))
pc = lightrec_emulate_block(state, block, pc);
/* Then compile it using the profiled data */
- lightrec_compile_block(state->cstate, block);
+ err = lightrec_compile_block(state->cstate, block);
+ if (err) {
+ state->exit_flags = LIGHTREC_EXIT_NOMEM;
+ return NULL;
+ }
} else {
lightrec_recompiler_add(state->rec, block);
}
return state->target_cycle - state->current_cycle;
}
+static void * lightrec_alloc_code(struct lightrec_state *state, size_t size)
+{
+ void *code;
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_lock(state);
+
+ code = tlsf_malloc(state->tlsf, size);
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_unlock(state);
+
+ return code;
+}
+
+static void lightrec_realloc_code(struct lightrec_state *state,
+ void *ptr, size_t size)
+{
+ /* NOTE: 'size' MUST be smaller than the size specified during
+ * the allocation. */
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_lock(state);
+
+ tlsf_realloc(state->tlsf, ptr, size);
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_unlock(state);
+}
+
+static void lightrec_free_code(struct lightrec_state *state, void *ptr)
+{
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_lock(state);
+
+ tlsf_free(state->tlsf, ptr);
+
+ if (ENABLE_THREADED_COMPILER)
+ lightrec_code_alloc_unlock(state);
+}
+
static void * lightrec_emit_code(struct lightrec_state *state,
+ const struct block *block,
jit_state_t *_jit, unsigned int *size)
{
bool has_code_buffer = ENABLE_CODE_BUFFER && state->tlsf;
if (has_code_buffer) {
jit_get_code(&code_size);
- code = tlsf_malloc(state->tlsf, (size_t) code_size);
- if (!code)
- return NULL;
+ code = lightrec_alloc_code(state, (size_t) code_size);
+
+ if (!code) {
+ if (ENABLE_THREADED_COMPILER) {
+ /* If we're using the threaded compiler, return
+ * an allocation error here. The threaded
+ * compiler will then empty its job queue and
+ * request a code flush using the reaper. */
+ return NULL;
+ }
+
+ /* Remove outdated blocks, and try again */
+ lightrec_remove_outdated_blocks(state->block_cache, block);
+
+ pr_debug("Re-try to alloc %zu bytes...\n", code_size);
+
+ code = lightrec_alloc_code(state, code_size);
+ if (!code) {
+ pr_err("Could not alloc even after removing old blocks!\n");
+ return NULL;
+ }
+ }
jit_set_code(code, code_size);
}
lightrec_register(MEM_FOR_CODE, new_code_size);
if (has_code_buffer) {
- tlsf_realloc(state->tlsf, code, new_code_size);
+ lightrec_realloc_code(state, code, (size_t) new_code_size);
pr_debug("Creating code block at address 0x%" PRIxPTR ", "
"code size: %" PRIuPTR " new: %" PRIuPTR "\n",
block->flags = 0;
block->nb_ops = 0;
- block->function = lightrec_emit_code(state, _jit,
+ block->function = lightrec_emit_code(state, block, _jit,
&block->code_size);
if (!block->function)
goto err_free_block;
block->flags = 0;
block->nb_ops = 0;
- block->function = lightrec_emit_code(state, _jit,
+ block->function = lightrec_emit_code(state, block, _jit,
&block->code_size);
if (!block->function)
goto err_free_block;
block->next = NULL;
block->flags = 0;
block->code_size = 0;
+ block->precompile_date = state->current_cycle;
#if ENABLE_THREADED_COMPILER
block->op_list_freed = (atomic_flag)ATOMIC_FLAG_INIT;
#endif
case OP_SWC2:
if (!LIGHTREC_FLAGS_GET_IO_MODE(op->flags))
return false;
- default: /* fall-through */
+ fallthrough;
+ default:
continue;
}
}
{
if (ENABLE_CODE_BUFFER && state->tlsf) {
pr_debug("Freeing code block at 0x%" PRIxPTR "\n", (uintptr_t) fn);
- tlsf_free(state->tlsf, fn);
+ lightrec_free_code(state, fn);
}
}
jit_state_t *_jit, *oldjit;
jit_node_t *start_of_block;
bool skip_next = false;
- void *old_fn;
+ void *old_fn, *new_fn;
unsigned int i, j;
u32 offset;
jit_ret();
jit_epilog();
- block->function = lightrec_emit_code(state, _jit,
- &block->code_size);
- if (!block->function) {
- pr_err("Unable to compile block!\n");
+ new_fn = lightrec_emit_code(state, block, _jit, &block->code_size);
+ if (!new_fn) {
+ if (!ENABLE_THREADED_COMPILER)
+ pr_err("Unable to compile block!\n");
+ block->_jit = oldjit;
+ _jit_destroy_state(_jit);
+ return -ENOMEM;
}
+ block->function = new_fn;
block->flags &= ~BLOCK_SHOULD_RECOMPILE;
/* Add compiled function to the LUT */
lightrec_free_opcode_list(state, block);
if (block->_jit)
_jit_destroy_state(block->_jit);
- lightrec_free_function(state, block->function);
- lightrec_unregister(MEM_FOR_CODE, block->code_size);
+ if (block->function) {
+ lightrec_free_function(state, block->function);
+ lightrec_unregister(MEM_FOR_CODE, block->code_size);
+ }
lightrec_free(state, MEM_FOR_IR, sizeof(*block), block);
}
size_t nb,
const struct lightrec_ops *ops)
{
- const struct lightrec_mem_map *codebuf_map;
+ const struct lightrec_mem_map *codebuf_map = &map[PSX_MAP_CODE_BUFFER];
struct lightrec_state *state;
uintptr_t addr;
void *tlsf = NULL;
return NULL;
}
- if (ENABLE_CODE_BUFFER && nb > PSX_MAP_CODE_BUFFER) {
- codebuf_map = &map[PSX_MAP_CODE_BUFFER];
-
+ if (ENABLE_CODE_BUFFER && nb > PSX_MAP_CODE_BUFFER
+ && codebuf_map->address) {
tlsf = tlsf_create_with_pool(codebuf_map->address,
codebuf_map->length);
if (!tlsf) {
state->tlsf = tlsf;
state->with_32bit_lut = with_32bit_lut;
-#if ENABLE_TINYMM
- state->tinymm = tinymm_init(malloc, free, 4096);
- if (!state->tinymm)
- goto err_free_state;
-#endif
-
state->block_cache = lightrec_blockcache_init(state);
if (!state->block_cache)
- goto err_free_tinymm;
+ goto err_free_state;
if (ENABLE_THREADED_COMPILER) {
state->rec = lightrec_recompiler_init(state);
lightrec_free_cstate(state->cstate);
err_free_block_cache:
lightrec_free_block_cache(state->block_cache);
-err_free_tinymm:
-#if ENABLE_TINYMM
- tinymm_shutdown(state->tinymm);
err_free_state:
-#endif
lightrec_unregister(MEM_FOR_LIGHTREC, sizeof(*state) +
lut_elm_size(state) * CODE_LUT_SIZE);
free(state);
if (ENABLE_CODE_BUFFER && state->tlsf)
tlsf_destroy(state->tlsf);
-#if ENABLE_TINYMM
- tinymm_shutdown(state->tinymm);
-#endif
lightrec_unregister(MEM_FOR_LIGHTREC, sizeof(*state) +
lut_elm_size(state) * CODE_LUT_SIZE);
free(state);
void lightrec_invalidate(struct lightrec_state *state, u32 addr, u32 len)
{
u32 kaddr = kunseg(addr & ~0x3);
- const struct lightrec_mem_map *map = lightrec_get_map(state, NULL, kaddr);
-
- if (map) {
- if (map != &state->maps[PSX_MAP_KERNEL_USER_RAM])
- return;
+ enum psx_map idx = lightrec_get_map_idx(state, kaddr);
+ switch (idx) {
+ case PSX_MAP_MIRROR1:
+ case PSX_MAP_MIRROR2:
+ case PSX_MAP_MIRROR3:
/* Handle mirrors */
- kaddr &= (state->maps[PSX_MAP_KERNEL_USER_RAM].length - 1);
-
- lightrec_invalidate_map(state, map, kaddr, len);
+ kaddr &= RAM_SIZE - 1;
+ fallthrough;
+ case PSX_MAP_KERNEL_USER_RAM:
+ break;
+ default:
+ return;
}
+
+ memset(lut_address(state, lut_offset(kaddr)), 0,
+ ((len + 3) / 4) * lut_elm_size(state));
}
void lightrec_invalidate_all(struct lightrec_state *state)
#define LIGHTREC_EXIT_BREAK (1 << 1)
#define LIGHTREC_EXIT_SYSCALL (1 << 2)
#define LIGHTREC_EXIT_SEGFAULT (1 << 3)
+#define LIGHTREC_EXIT_NOMEM (1 << 4)
enum psx_map {
PSX_MAP_KERNEL_USER_RAM,
#include "memmanager.h"
#include <stdlib.h>
-#if ENABLE_TINYMM
-#include <tinymm.h>
-#endif
#ifdef ENABLE_THREADED_COMPILER
#include <stdatomic.h>
{
void *ptr;
-#if ENABLE_TINYMM
- if (type == MEM_FOR_IR)
- ptr = tinymm_malloc(state->tinymm, len);
- else
-#endif
- ptr = malloc(len);
+ ptr = malloc(len);
if (!ptr)
return NULL;
{
void *ptr;
-#if ENABLE_TINYMM
- if (type == MEM_FOR_IR)
- ptr = tinymm_zalloc(state->tinymm, len);
- else
-#endif
- ptr = calloc(1, len);
+ ptr = calloc(1, len);
if (!ptr)
return NULL;
enum mem_type type, unsigned int len, void *ptr)
{
lightrec_unregister(type, len);
-#if ENABLE_TINYMM
- if (type == MEM_FOR_IR)
- tinymm_free(state->tinymm, ptr);
- else
-#endif
- free(ptr);
+ free(ptr);
}
float lightrec_get_average_ipi(void)
break;
case OP_LUI:
- lightrec_modify_lui(block, i);
+ if (!prev || !has_delay_slot(prev->c))
+ lightrec_modify_lui(block, i);
lightrec_remove_useless_lui(block, i, known, values);
break;
op->i.op = OP_META_MOV;
op->r.rs = op->r.rt;
}
- case OP_SPECIAL_SUB: /* fall-through */
+ fallthrough;
+ case OP_SPECIAL_SUB:
case OP_SPECIAL_SUBU:
if (op->r.rt == 0) {
pr_debug("Convert OR/ADD/SUB $zero to MOV\n");
op->i.op = OP_META_MOV;
}
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
}
if (opcode_reads_register(next_op, op.r.rd) ||
opcode_writes_register(next_op, op.r.rd))
continue;
- case OP_SPECIAL_JR: /* fall-through */
+ fallthrough;
+ case OP_SPECIAL_JR:
if (opcode_writes_register(next_op, op.r.rs))
continue;
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
- case OP_J: /* fall-through */
+ fallthrough;
+ case OP_J:
break;
case OP_JAL:
if (opcode_reads_register(next_op, 31) ||
case OP_BNE:
if (op.i.rt && opcode_writes_register(next_op, op.i.rt))
continue;
- case OP_BLEZ: /* fall-through */
+ fallthrough;
+ case OP_BLEZ:
case OP_BGTZ:
if (op.i.rs && opcode_writes_register(next_op, op.i.rs))
continue;
if (opcode_reads_register(next_op, 31) ||
opcode_writes_register(next_op, 31))
continue;
- case OP_REGIMM_BLTZ: /* fall-through */
+ fallthrough;
+ case OP_REGIMM_BLTZ:
case OP_REGIMM_BGEZ:
if (op.i.rs &&
opcode_writes_register(next_op, op.i.rs))
continue;
break;
}
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
offset = i + 1 + (s16)list->i.imm;
if (offset >= 0 && offset < block->nb_ops)
break;
- default: /* fall-through */
+ fallthrough;
+ default:
continue;
}
list->flags |= LIGHTREC_SMC;
}
}
- case OP_SWL: /* fall-through */
+ fallthrough;
+ case OP_SWL:
case OP_SWR:
case OP_SWC2:
case OP_LB:
case PSX_MAP_KERNEL_USER_RAM:
if (val == kunseg_val)
list->flags |= LIGHTREC_NO_MASK;
- /* fall-through */
+ fallthrough;
case PSX_MAP_MIRROR1:
case PSX_MAP_MIRROR2:
case PSX_MAP_MIRROR3:
break;
}
}
- default: /* fall-through */
+ fallthrough;
+ default:
break;
}
}
break;
}
- /* fall-through */
+ fallthrough;
default:
continue;
}
return;
}
- /* fall-through */
+ fallthrough;
default:
break;
}
if (lightrec_always_skip_div_check() ||
(known & BIT(list->c.r.rt) && values[list->c.r.rt]))
list->flags |= LIGHTREC_NO_DIV_CHECK;
- case OP_SPECIAL_MULT: /* fall-through */
+ fallthrough;
+ case OP_SPECIAL_MULT:
case OP_SPECIAL_MULTU:
break;
default:
* Copyright (C) 2019-2021 Paul Cercueil <paul@crapouillou.net>
*/
+#include "blockcache.h"
#include "debug.h"
#include "interpreter.h"
#include "lightrec-private.h"
#include "memmanager.h"
+#include "reaper.h"
#include "slist.h"
#include <errno.h>
pthread_cond_t cond;
pthread_cond_t cond2;
pthread_mutex_t mutex;
- bool stop;
+ bool stop, must_flush;
struct slist_elm slist;
+ pthread_mutex_t alloc_mutex;
+
unsigned int nb_recs;
struct recompiler_thd thds[];
};
return NULL;
}
+static bool lightrec_cancel_block_rec(struct recompiler *rec,
+ struct block_rec *block_rec)
+{
+ if (block_rec->compiling) {
+ /* Block is being recompiled - wait for
+ * completion */
+ pthread_cond_wait(&rec->cond2, &rec->mutex);
+
+ /* We can't guarantee the signal was for us.
+ * Since block_rec may have been removed while
+ * we were waiting on the condition, we cannot
+ * check block_rec->compiling again. The best
+ * thing is just to restart the function. */
+ return false;
+ }
+
+ /* Block is not yet being processed - remove it from the list */
+ slist_remove(&rec->slist, &block_rec->slist);
+ lightrec_free(rec->state, MEM_FOR_LIGHTREC,
+ sizeof(*block_rec), block_rec);
+
+ return true;
+}
+
+static void lightrec_cancel_list(struct recompiler *rec)
+{
+ struct block_rec *block_rec;
+ struct slist_elm *next;
+
+ while (!!(next = lightrec_get_first_elm(&rec->slist))) {
+ block_rec = container_of(next, struct block_rec, slist);
+
+ lightrec_cancel_block_rec(rec, block_rec);
+ }
+
+ pthread_cond_broadcast(&rec->cond2);
+}
+
+static void lightrec_flush_code_buffer(struct lightrec_state *state, void *d)
+{
+ struct recompiler *rec = d;
+
+ pthread_mutex_lock(&rec->mutex);
+
+ if (rec->must_flush) {
+ lightrec_remove_outdated_blocks(state->block_cache, NULL);
+ rec->must_flush = false;
+ }
+
+ pthread_mutex_unlock(&rec->mutex);
+}
+
static void lightrec_compile_list(struct recompiler *rec,
struct recompiler_thd *thd)
{
if (likely(!(block->flags & BLOCK_IS_DEAD))) {
ret = lightrec_compile_block(thd->cstate, block);
+ if (ret == -ENOMEM) {
+ /* Code buffer is full. Request the reaper to
+ * flush it. */
+
+ pthread_mutex_lock(&rec->mutex);
+ if (!rec->must_flush) {
+ lightrec_reaper_add(rec->state->reaper,
+ lightrec_flush_code_buffer,
+ rec);
+ lightrec_cancel_list(rec);
+ rec->must_flush = true;
+ }
+ return;
+ }
+
if (ret) {
pr_err("Unable to compile block at PC 0x%x: %d\n",
block->pc, ret);
rec->state = state;
rec->stop = false;
+ rec->must_flush = false;
rec->nb_recs = nb_recs;
slist_init(&rec->slist);
goto err_cnd_destroy;
}
+ ret = pthread_mutex_init(&rec->alloc_mutex, NULL);
+ if (ret) {
+ pr_err("Cannot init alloc mutex variable: %d\n", ret);
+ goto err_cnd2_destroy;
+ }
+
ret = pthread_mutex_init(&rec->mutex, NULL);
if (ret) {
pr_err("Cannot init mutex variable: %d\n", ret);
- goto err_cnd2_destroy;
+ goto err_alloc_mtx_destroy;
}
for (i = 0; i < nb_recs; i++) {
err_mtx_destroy:
pthread_mutex_destroy(&rec->mutex);
+err_alloc_mtx_destroy:
+ pthread_mutex_destroy(&rec->alloc_mutex);
err_cnd2_destroy:
pthread_cond_destroy(&rec->cond2);
err_cnd_destroy:
/* Stop the thread */
pthread_mutex_lock(&rec->mutex);
pthread_cond_broadcast(&rec->cond);
+ lightrec_cancel_list(rec);
pthread_mutex_unlock(&rec->mutex);
for (i = 0; i < rec->nb_recs; i++)
lightrec_free_cstate(rec->thds[i].cstate);
pthread_mutex_destroy(&rec->mutex);
+ pthread_mutex_destroy(&rec->alloc_mutex);
pthread_cond_destroy(&rec->cond);
pthread_cond_destroy(&rec->cond2);
lightrec_free(rec->state, MEM_FOR_LIGHTREC, sizeof(*rec), rec);
pthread_mutex_lock(&rec->mutex);
+ /* If the recompiler must flush the code cache, we can't add the new
+ * job. It will be re-added next time the block's address is jumped to
+ * again. */
+ if (rec->must_flush)
+ goto out_unlock;
+
/* 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)
for (elm = slist_first(&rec->slist); elm; elm = elm->next) {
block_rec = container_of(elm, struct block_rec, slist);
- if (block_rec->block != block)
- continue;
-
- if (block_rec->compiling) {
- /* Block is being recompiled - wait for
- * completion */
- pthread_cond_wait(&rec->cond2, &rec->mutex);
+ if (block_rec->block == block) {
+ if (lightrec_cancel_block_rec(rec, block_rec))
+ goto out_unlock;
- /* We can't guarantee the signal was for us.
- * Since block_rec may have been removed while
- * we were waiting on the condition, we cannot
- * check block_rec->compiling again. The best
- * thing is just to restart the function. */
break;
- } else {
- /* Block is not yet being processed - remove it
- * from the list */
- slist_remove(&rec->slist, elm);
- lightrec_free(rec->state, MEM_FOR_LIGHTREC,
- sizeof(*block_rec), block_rec);
-
- goto out_unlock;
}
}
return NULL;
}
+
+void lightrec_code_alloc_lock(struct lightrec_state *state)
+{
+ pthread_mutex_lock(&state->rec->alloc_mutex);
+}
+
+void lightrec_code_alloc_unlock(struct lightrec_state *state)
+{
+ pthread_mutex_unlock(&state->rec->alloc_mutex);
+}
void * lightrec_recompiler_run_first_pass(struct lightrec_state *state,
struct block *block, u32 *pc);
+void lightrec_code_alloc_lock(struct lightrec_state *state);
+void lightrec_code_alloc_unlock(struct lightrec_state *state);
+
#endif /* __LIGHTREC_RECOMPILER_H__ */