target/hppa: Convert to TranslatorOps

Signed-off-by: Richard Henderson <rth@twiddle.net>
master
Richard Henderson 2017-07-14 22:25:35 -10:00 committed by Richard Henderson
parent d01a362528
commit 51b061fbf0
1 changed files with 161 additions and 145 deletions

View File

@ -3729,185 +3729,201 @@ static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn)
return gen_illegal(ctx); return gen_illegal(ctx);
} }
void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb) static int hppa_tr_init_disas_context(DisasContextBase *dcbase,
CPUState *cs, int max_insns)
{ {
CPUHPPAState *env = cs->env_ptr; DisasContext *ctx = container_of(dcbase, DisasContext, base);
DisasContext ctx; TranslationBlock *tb = ctx->base.tb;
DisasJumpType ret; int i, bound;
int num_insns, max_insns, i;
ctx.base.tb = tb; ctx->cs = cs;
ctx.base.singlestep_enabled = cs->singlestep_enabled; ctx->iaoq_f = tb->pc;
ctx.cs = cs; ctx->iaoq_b = tb->cs_base;
ctx.iaoq_f = tb->pc; ctx->iaoq_n = -1;
ctx.iaoq_b = tb->cs_base; TCGV_UNUSED(ctx->iaoq_n_var);
ctx.ntemps = 0; ctx->ntemps = 0;
for (i = 0; i < ARRAY_SIZE(ctx.temps); ++i) { for (i = 0; i < ARRAY_SIZE(ctx->temps); ++i) {
TCGV_UNUSED(ctx.temps[i]); TCGV_UNUSED(ctx->temps[i]);
} }
/* Compute the maximum number of insns to execute, as bounded by bound = -(tb->pc | TARGET_PAGE_MASK) / 4;
(1) icount, (2) single-stepping, (3) branch delay slots, or return MIN(max_insns, bound);
(4) the number of insns remaining on the current page. */ }
max_insns = tb->cflags & CF_COUNT_MASK;
if (max_insns == 0) {
max_insns = CF_COUNT_MASK;
}
if (ctx.base.singlestep_enabled || singlestep) {
max_insns = 1;
} else if (max_insns > TCG_MAX_INSNS) {
max_insns = TCG_MAX_INSNS;
}
num_insns = 0; static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
gen_tb_start(tb); {
DisasContext *ctx = container_of(dcbase, DisasContext, base);
/* Seed the nullification status from PSW[N], as shown in TB->FLAGS. */ /* Seed the nullification status from PSW[N], as shown in TB->FLAGS. */
ctx.null_cond = cond_make_f(); ctx->null_cond = cond_make_f();
ctx.psw_n_nonzero = false; ctx->psw_n_nonzero = false;
if (tb->flags & 1) { if (ctx->base.tb->flags & 1) {
ctx.null_cond.c = TCG_COND_ALWAYS; ctx->null_cond.c = TCG_COND_ALWAYS;
ctx.psw_n_nonzero = true; ctx->psw_n_nonzero = true;
} }
ctx.null_lab = NULL; ctx->null_lab = NULL;
}
do { static void hppa_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
tcg_gen_insn_start(ctx.iaoq_f, ctx.iaoq_b); {
num_insns++; DisasContext *ctx = container_of(dcbase, DisasContext, base);
if (unlikely(cpu_breakpoint_test(cs, ctx.iaoq_f, BP_ANY))) { tcg_gen_insn_start(ctx->iaoq_f, ctx->iaoq_b);
ret = gen_excp(&ctx, EXCP_DEBUG); }
break;
}
if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) {
gen_io_start();
}
if (ctx.iaoq_f < TARGET_PAGE_SIZE) { static bool hppa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
ret = do_page_zero(&ctx); const CPUBreakpoint *bp)
assert(ret != DISAS_NEXT); {
DisasContext *ctx = container_of(dcbase, DisasContext, base);
ctx->base.is_jmp = gen_excp(ctx, EXCP_DEBUG);
ctx->base.pc_next = ctx->iaoq_f + 4;
return true;
}
static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
CPUHPPAState *env = cs->env_ptr;
DisasJumpType ret;
int i, n;
/* Execute one insn. */
if (ctx->iaoq_f < TARGET_PAGE_SIZE) {
ret = do_page_zero(ctx);
assert(ret != DISAS_NEXT);
} else {
/* Always fetch the insn, even if nullified, so that we check
the page permissions for execute. */
uint32_t insn = cpu_ldl_code(env, ctx->iaoq_f);
/* Set up the IA queue for the next insn.
This will be overwritten by a branch. */
if (ctx->iaoq_b == -1) {
ctx->iaoq_n = -1;
ctx->iaoq_n_var = get_temp(ctx);
tcg_gen_addi_tl(ctx->iaoq_n_var, cpu_iaoq_b, 4);
} else { } else {
/* Always fetch the insn, even if nullified, so that we check ctx->iaoq_n = ctx->iaoq_b + 4;
the page permissions for execute. */ TCGV_UNUSED(ctx->iaoq_n_var);
uint32_t insn = cpu_ldl_code(env, ctx.iaoq_f);
/* Set up the IA queue for the next insn.
This will be overwritten by a branch. */
if (ctx.iaoq_b == -1) {
ctx.iaoq_n = -1;
ctx.iaoq_n_var = get_temp(&ctx);
tcg_gen_addi_tl(ctx.iaoq_n_var, cpu_iaoq_b, 4);
} else {
ctx.iaoq_n = ctx.iaoq_b + 4;
TCGV_UNUSED(ctx.iaoq_n_var);
}
if (unlikely(ctx.null_cond.c == TCG_COND_ALWAYS)) {
ctx.null_cond.c = TCG_COND_NEVER;
ret = DISAS_NEXT;
} else {
ret = translate_one(&ctx, insn);
assert(ctx.null_lab == NULL);
}
} }
for (i = 0; i < ctx.ntemps; ++i) { if (unlikely(ctx->null_cond.c == TCG_COND_ALWAYS)) {
tcg_temp_free(ctx.temps[i]); ctx->null_cond.c = TCG_COND_NEVER;
TCGV_UNUSED(ctx.temps[i]); ret = DISAS_NEXT;
} else {
ret = translate_one(ctx, insn);
assert(ctx->null_lab == NULL);
} }
ctx.ntemps = 0;
/* If we see non-linear instructions, exhaust instruction count,
or run out of buffer space, stop generation. */
/* ??? The non-linear instruction restriction is purely due to
the debugging dump. Otherwise we *could* follow unconditional
branches within the same page. */
if (ret == DISAS_NEXT
&& (ctx.iaoq_b != ctx.iaoq_f + 4
|| num_insns >= max_insns
|| tcg_op_buf_full())) {
if (ctx.null_cond.c == TCG_COND_NEVER
|| ctx.null_cond.c == TCG_COND_ALWAYS) {
nullify_set(&ctx, ctx.null_cond.c == TCG_COND_ALWAYS);
gen_goto_tb(&ctx, 0, ctx.iaoq_b, ctx.iaoq_n);
ret = DISAS_NORETURN;
} else {
ret = DISAS_IAQ_N_STALE;
}
}
ctx.iaoq_f = ctx.iaoq_b;
ctx.iaoq_b = ctx.iaoq_n;
if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
break;
}
if (ctx.iaoq_f == -1) {
tcg_gen_mov_tl(cpu_iaoq_f, cpu_iaoq_b);
copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_n, ctx.iaoq_n_var);
nullify_save(&ctx);
ret = DISAS_IAQ_N_UPDATED;
break;
}
if (ctx.iaoq_b == -1) {
tcg_gen_mov_tl(cpu_iaoq_b, ctx.iaoq_n_var);
}
} while (ret == DISAS_NEXT);
if (tb->cflags & CF_LAST_IO) {
gen_io_end();
} }
switch (ret) { /* Free any temporaries allocated. */
for (i = 0, n = ctx->ntemps; i < n; ++i) {
tcg_temp_free(ctx->temps[i]);
TCGV_UNUSED(ctx->temps[i]);
}
ctx->ntemps = 0;
/* Advance the insn queue. */
/* ??? The non-linear instruction restriction is purely due to
the debugging dump. Otherwise we *could* follow unconditional
branches within the same page. */
if (ret == DISAS_NEXT && ctx->iaoq_b != ctx->iaoq_f + 4) {
if (ctx->null_cond.c == TCG_COND_NEVER
|| ctx->null_cond.c == TCG_COND_ALWAYS) {
nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS);
gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n);
ret = DISAS_NORETURN;
} else {
ret = DISAS_IAQ_N_STALE;
}
}
ctx->iaoq_f = ctx->iaoq_b;
ctx->iaoq_b = ctx->iaoq_n;
ctx->base.is_jmp = ret;
if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) {
return;
}
if (ctx->iaoq_f == -1) {
tcg_gen_mov_tl(cpu_iaoq_f, cpu_iaoq_b);
copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
nullify_save(ctx);
ctx->base.is_jmp = DISAS_IAQ_N_UPDATED;
} else if (ctx->iaoq_b == -1) {
tcg_gen_mov_tl(cpu_iaoq_b, ctx->iaoq_n_var);
}
}
static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
switch (ctx->base.is_jmp) {
case DISAS_NORETURN: case DISAS_NORETURN:
break; break;
case DISAS_TOO_MANY:
case DISAS_IAQ_N_STALE: case DISAS_IAQ_N_STALE:
copy_iaoq_entry(cpu_iaoq_f, ctx.iaoq_f, cpu_iaoq_f); copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f);
copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_b, cpu_iaoq_b); copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b);
nullify_save(&ctx); nullify_save(ctx);
/* FALLTHRU */ /* FALLTHRU */
case DISAS_IAQ_N_UPDATED: case DISAS_IAQ_N_UPDATED:
if (ctx.base.singlestep_enabled) { if (ctx->base.singlestep_enabled) {
gen_excp_1(EXCP_DEBUG); gen_excp_1(EXCP_DEBUG);
} else { } else {
tcg_gen_lookup_and_goto_ptr(cpu_iaoq_f); tcg_gen_lookup_and_goto_ptr(cpu_iaoq_f);
} }
break; break;
default: default:
abort(); g_assert_not_reached();
} }
gen_tb_end(tb, num_insns); /* We don't actually use this during normal translation,
but we should interact with the generic main loop. */
ctx->base.pc_next = ctx->base.tb->pc + 4 * ctx->base.num_insns;
}
tb->size = num_insns * 4; static void hppa_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs)
tb->icount = num_insns; {
TranslationBlock *tb = dcbase->tb;
#ifdef DEBUG_DISAS switch (tb->pc) {
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) case 0x00:
&& qemu_log_in_addr_range(tb->pc)) { qemu_log("IN:\n0x00000000: (null)\n");
qemu_log_lock(); break;
switch (tb->pc) { case 0xb0:
case 0x00: qemu_log("IN:\n0x000000b0: light-weight-syscall\n");
qemu_log("IN:\n0x00000000: (null)\n\n"); break;
break; case 0xe0:
case 0xb0: qemu_log("IN:\n0x000000e0: set-thread-pointer-syscall\n");
qemu_log("IN:\n0x000000b0: light-weight-syscall\n\n"); break;
break; case 0x100:
case 0xe0: qemu_log("IN:\n0x00000100: syscall\n");
qemu_log("IN:\n0x000000e0: set-thread-pointer-syscall\n\n"); break;
break; default:
case 0x100: qemu_log("IN: %s\n", lookup_symbol(tb->pc));
qemu_log("IN:\n0x00000100: syscall\n\n"); log_target_disas(cs, tb->pc, tb->size, 1);
break; break;
default:
qemu_log("IN: %s\n", lookup_symbol(tb->pc));
log_target_disas(cs, tb->pc, tb->size, 1);
qemu_log("\n");
break;
}
qemu_log_unlock();
} }
#endif }
static const TranslatorOps hppa_tr_ops = {
.init_disas_context = hppa_tr_init_disas_context,
.tb_start = hppa_tr_tb_start,
.insn_start = hppa_tr_insn_start,
.breakpoint_check = hppa_tr_breakpoint_check,
.translate_insn = hppa_tr_translate_insn,
.tb_stop = hppa_tr_tb_stop,
.disas_log = hppa_tr_disas_log,
};
void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
{
DisasContext ctx;
translator_loop(&hppa_tr_ops, &ctx.base, cs, tb);
} }
void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb, void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb,