[subrepo]
remote = https://github.com/pcercuei/lightrec.git
branch = master
- commit = b8ce1f3dab45d7c665aa406a0ff183ae6565205d
- parent = 305945333b4ec7d6910a077278c850b1cd887057
+ commit = d88760e40c1d2a5698c7b6f6a53cce31fda799f0
+ parent = 963f41620dce6ddb2527b7e3dced09564031f783
method = merge
cmdver = 0.4.6
option(ENABLE_FIRST_PASS "Run the interpreter as first-pass optimization" ON)
-option(ENABLE_THREADED_COMPILER "Enable threaded compiler" OFF)
+option(ENABLE_THREADED_COMPILER "Enable threaded compiler" ON)
if (ENABLE_THREADED_COMPILER)
list(APPEND LIGHTREC_SOURCES recompiler.c reaper.c)
}
if (has_ds && op_flag_load_delay(ds->flags)
- && opcode_is_load(ds->c) && !state->no_load_delay) {
+ && opcode_has_load_delay(ds->c) && !state->no_load_delay) {
/* If the delay slot is a load opcode, its target register
* will be written after the first opcode of the target is
* executed. Handle this by jumping to a special section of
* interpreter in that case.
* Same goes for when we have a branch in a delay slot of another
* branch. */
- load_in_ds = opcode_is_load(op->c) || opcode_is_mfc(op->c);
+ load_in_ds = opcode_has_load_delay(op->c);
branch_in_ds = has_delay_slot(op->c);
if (branch) {
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;
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;
was_dead[i / 32] &= ~BIT(i % 32);
}
+ 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)
offset = lut_offset(block->pc) + target->offset;
lut_write(state, offset, jit_address(target->label));
- offset = block->pc + target->offset * sizeof(u32);
- block2 = lightrec_find_block(state->block_cache, offset);
+ 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);
return reg_is_read(list, a, b, reg) || reg_is_written(list, a, b, reg);
}
-bool opcode_is_mfc(union code op)
+static bool opcode_is_mfc(union code op)
{
switch (op.i.op) {
case OP_CP0:
return false;
}
-bool opcode_is_load(union code op)
+static bool opcode_is_load(union code op)
{
switch (op.i.op) {
case OP_LB:
}
}
+bool opcode_has_load_delay(union code op)
+{
+ return (opcode_is_load(op) && op.i.rt && op.i.op != OP_LWC2)
+ || opcode_is_mfc(op);
+}
+
static u8 opcode_get_io_size(union code op)
{
switch (op.i.op) {
for (i = 0; i < block->nb_ops; i++) {
op = &list[i];
- if (!opcode_is_load(op->c) || !op->c.i.rt || op->c.i.op == OP_LWC2)
+ if (!opcode_has_load_delay(op->c))
continue;
if (!is_delay_slot(list, i)) {
__cnst u64 opcode_write_mask(union code op);
__cnst _Bool has_delay_slot(union code op);
_Bool is_delay_slot(const struct opcode *list, unsigned int offset);
-__cnst _Bool opcode_is_mfc(union code op);
-__cnst _Bool opcode_is_load(union code op);
+__cnst _Bool opcode_has_load_delay(union code op);
__cnst _Bool opcode_is_io(union code op);
__cnst _Bool is_unconditional_jump(union code c);
__cnst _Bool is_syscall(union code c);
struct block_rec {
struct block *block;
struct slist_elm slist;
+ unsigned int requests;
bool compiling;
};
return nb < 1 ? 1 : nb;
}
-static struct slist_elm * lightrec_get_first_elm(struct slist_elm *head)
+static struct block_rec * lightrec_get_best_elm(struct slist_elm *head)
{
- struct block_rec *block_rec;
+ struct block_rec *block_rec, *best = NULL;
struct slist_elm *elm;
for (elm = slist_first(head); elm; elm = elm->next) {
block_rec = container_of(elm, struct block_rec, slist);
- if (!block_rec->compiling)
- return elm;
+ if (!block_rec->compiling
+ && (!best || block_rec->requests > best->requests))
+ best = block_rec;
}
- return NULL;
+ return best;
}
static bool lightrec_cancel_block_rec(struct recompiler *rec,
struct recompiler_thd *thd)
{
struct block_rec *block_rec;
- struct slist_elm *next;
struct block *block;
int ret;
- while (!!(next = lightrec_get_first_elm(&rec->slist))) {
- block_rec = container_of(next, struct block_rec, slist);
+ while (!!(block_rec = lightrec_get_best_elm(&rec->slist))) {
block_rec->compiling = true;
block = block_rec->block;
pthread_mutex_lock(&rec->mutex);
- slist_remove(&rec->slist, next);
+ slist_remove(&rec->slist, &block_rec->slist);
lightrec_free(rec->state, MEM_FOR_LIGHTREC,
sizeof(*block_rec), block_rec);
pthread_cond_broadcast(&rec->cond2);
int lightrec_recompiler_add(struct recompiler *rec, struct block *block)
{
- struct slist_elm *elm, *prev;
+ struct slist_elm *elm;
struct block_rec *block_rec;
+ u32 pc1, pc2;
int ret = 0;
pthread_mutex_lock(&rec->mutex);
if (block_has_flag(block, BLOCK_IS_DEAD))
goto out_unlock;
- for (elm = slist_first(&rec->slist), prev = NULL; elm;
- prev = elm, elm = elm->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) {
- /* The block to compile is already in the queue - bump
- * it to the top of the list, unless the block is being
- * recompiled. */
- if (prev && !block_rec->compiling &&
- !block_has_flag(block, BLOCK_SHOULD_RECOMPILE)) {
- slist_remove_next(prev);
- slist_append(&rec->slist, elm);
- }
+ /* The block to compile is already in the queue -
+ * increment its counter to increase its priority */
+ block_rec->requests++;
+ goto out_unlock;
+ }
+ pc1 = kunseg(block_rec->block->pc);
+ pc2 = kunseg(block->pc);
+ if (pc2 >= pc1 && pc2 < pc1 + block_rec->block->nb_ops * 4) {
+ /* The block we want to compile is already covered by
+ * another one in the queue - increment its counter to
+ * increase its priority */
+ block_rec->requests++;
goto out_unlock;
}
}
block_rec->block = block;
block_rec->compiling = false;
+ block_rec->requests = 1;
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_has_flag(block, BLOCK_SHOULD_RECOMPILE))
- for (; elm->next; elm = elm->next);
-
+ /* Push the new entry to the front of the queue */
slist_append(elm, &block_rec->slist);
/* Signal the thread */