target/ppc: convert to TranslatorOps
A few changes worth noting: - Didn't migrate ctx->exception to DISAS_* since the exception field is in many cases architecturally relevant. - Moved the cross-page check from the end of translate_insn to tb_start. - Removed the exit(1) after a TCG temp leak; changed the fprintf there to qemu_log. Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Emilio G. Cota <cota@braap.org> Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
This commit is contained in:
parent
b6bac4bc70
commit
b0c2d5213a
@ -7207,217 +7207,222 @@ void ppc_cpu_dump_statistics(CPUState *cs, FILE*f,
|
||||
#endif
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
|
||||
static int ppc_tr_init_disas_context(DisasContextBase *dcbase,
|
||||
CPUState *cs, int max_insns)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
CPUPPCState *env = cs->env_ptr;
|
||||
DisasContext ctx, *ctxp = &ctx;
|
||||
opc_handler_t **table, *handler;
|
||||
int max_insns;
|
||||
int bound;
|
||||
|
||||
ctx.base.singlestep_enabled = cs->singlestep_enabled;
|
||||
ctx.base.tb = tb;
|
||||
ctx.base.pc_first = tb->pc;
|
||||
ctx.base.pc_next = tb->pc; /* nip */
|
||||
ctx.base.num_insns = 0;
|
||||
|
||||
ctx.exception = POWERPC_EXCP_NONE;
|
||||
ctx.spr_cb = env->spr_cb;
|
||||
ctx.pr = msr_pr;
|
||||
ctx.mem_idx = env->dmmu_idx;
|
||||
ctx.dr = msr_dr;
|
||||
ctx->exception = POWERPC_EXCP_NONE;
|
||||
ctx->spr_cb = env->spr_cb;
|
||||
ctx->pr = msr_pr;
|
||||
ctx->mem_idx = env->dmmu_idx;
|
||||
ctx->dr = msr_dr;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
ctx.hv = msr_hv || !env->has_hv_mode;
|
||||
ctx->hv = msr_hv || !env->has_hv_mode;
|
||||
#endif
|
||||
ctx.insns_flags = env->insns_flags;
|
||||
ctx.insns_flags2 = env->insns_flags2;
|
||||
ctx.access_type = -1;
|
||||
ctx.need_access_type = !(env->mmu_model & POWERPC_MMU_64B);
|
||||
ctx.le_mode = !!(env->hflags & (1 << MSR_LE));
|
||||
ctx.default_tcg_memop_mask = ctx.le_mode ? MO_LE : MO_BE;
|
||||
ctx->insns_flags = env->insns_flags;
|
||||
ctx->insns_flags2 = env->insns_flags2;
|
||||
ctx->access_type = -1;
|
||||
ctx->need_access_type = !(env->mmu_model & POWERPC_MMU_64B);
|
||||
ctx->le_mode = !!(env->hflags & (1 << MSR_LE));
|
||||
ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE;
|
||||
#if defined(TARGET_PPC64)
|
||||
ctx.sf_mode = msr_is_64bit(env, env->msr);
|
||||
ctx.has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
||||
ctx->sf_mode = msr_is_64bit(env, env->msr);
|
||||
ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR);
|
||||
#endif
|
||||
if (env->mmu_model == POWERPC_MMU_32B ||
|
||||
env->mmu_model == POWERPC_MMU_601 ||
|
||||
(env->mmu_model & POWERPC_MMU_64B))
|
||||
ctx.lazy_tlb_flush = true;
|
||||
ctx->lazy_tlb_flush = true;
|
||||
|
||||
ctx.fpu_enabled = !!msr_fp;
|
||||
ctx->fpu_enabled = !!msr_fp;
|
||||
if ((env->flags & POWERPC_FLAG_SPE) && msr_spe)
|
||||
ctx.spe_enabled = !!msr_spe;
|
||||
ctx->spe_enabled = !!msr_spe;
|
||||
else
|
||||
ctx.spe_enabled = false;
|
||||
ctx->spe_enabled = false;
|
||||
if ((env->flags & POWERPC_FLAG_VRE) && msr_vr)
|
||||
ctx.altivec_enabled = !!msr_vr;
|
||||
ctx->altivec_enabled = !!msr_vr;
|
||||
else
|
||||
ctx.altivec_enabled = false;
|
||||
ctx->altivec_enabled = false;
|
||||
if ((env->flags & POWERPC_FLAG_VSX) && msr_vsx) {
|
||||
ctx.vsx_enabled = !!msr_vsx;
|
||||
ctx->vsx_enabled = !!msr_vsx;
|
||||
} else {
|
||||
ctx.vsx_enabled = false;
|
||||
ctx->vsx_enabled = false;
|
||||
}
|
||||
#if defined(TARGET_PPC64)
|
||||
if ((env->flags & POWERPC_FLAG_TM) && msr_tm) {
|
||||
ctx.tm_enabled = !!msr_tm;
|
||||
ctx->tm_enabled = !!msr_tm;
|
||||
} else {
|
||||
ctx.tm_enabled = false;
|
||||
ctx->tm_enabled = false;
|
||||
}
|
||||
#endif
|
||||
ctx.gtse = !!(env->spr[SPR_LPCR] & LPCR_GTSE);
|
||||
ctx->gtse = !!(env->spr[SPR_LPCR] & LPCR_GTSE);
|
||||
if ((env->flags & POWERPC_FLAG_SE) && msr_se)
|
||||
ctx.singlestep_enabled = CPU_SINGLE_STEP;
|
||||
ctx->singlestep_enabled = CPU_SINGLE_STEP;
|
||||
else
|
||||
ctx.singlestep_enabled = 0;
|
||||
ctx->singlestep_enabled = 0;
|
||||
if ((env->flags & POWERPC_FLAG_BE) && msr_be)
|
||||
ctx.singlestep_enabled |= CPU_BRANCH_STEP;
|
||||
if (unlikely(ctx.base.singlestep_enabled)) {
|
||||
ctx.singlestep_enabled |= GDBSTUB_SINGLE_STEP;
|
||||
ctx->singlestep_enabled |= CPU_BRANCH_STEP;
|
||||
if (unlikely(ctx->base.singlestep_enabled)) {
|
||||
ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP;
|
||||
}
|
||||
#if defined (DO_SINGLE_STEP) && 0
|
||||
/* Single step trace mode */
|
||||
msr_se = 1;
|
||||
#endif
|
||||
ctx.base.num_insns = 0;
|
||||
max_insns = tb_cflags(tb) & CF_COUNT_MASK;
|
||||
if (max_insns == 0) {
|
||||
max_insns = CF_COUNT_MASK;
|
||||
|
||||
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
|
||||
return MIN(max_insns, bound);
|
||||
}
|
||||
|
||||
static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs)
|
||||
{
|
||||
}
|
||||
|
||||
static void ppc_tr_insn_start(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
tcg_gen_insn_start(dcbase->pc_next);
|
||||
}
|
||||
|
||||
static bool ppc_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs,
|
||||
const CPUBreakpoint *bp)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
gen_debug_exception(ctx);
|
||||
/* The address covered by the breakpoint must be included in
|
||||
[tb->pc, tb->pc + tb->size) in order to for it to be
|
||||
properly cleared -- thus we increment the PC here so that
|
||||
the logic setting tb->size below does the right thing. */
|
||||
ctx->base.pc_next += 4;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
CPUPPCState *env = cs->env_ptr;
|
||||
opc_handler_t **table, *handler;
|
||||
|
||||
LOG_DISAS("----------------\n");
|
||||
LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n",
|
||||
ctx->base.pc_next, ctx->mem_idx, (int)msr_ir);
|
||||
|
||||
if (unlikely(need_byteswap(ctx))) {
|
||||
ctx->opcode = bswap32(cpu_ldl_code(env, ctx->base.pc_next));
|
||||
} else {
|
||||
ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next);
|
||||
}
|
||||
if (max_insns > TCG_MAX_INSNS) {
|
||||
max_insns = TCG_MAX_INSNS;
|
||||
}
|
||||
|
||||
gen_tb_start(tb);
|
||||
tcg_clear_temp_count();
|
||||
/* Set env in case of segfault during code fetch */
|
||||
while (ctx.exception == POWERPC_EXCP_NONE && !tcg_op_buf_full()) {
|
||||
tcg_gen_insn_start(ctx.base.pc_next);
|
||||
ctx.base.num_insns++;
|
||||
|
||||
if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) {
|
||||
gen_debug_exception(ctxp);
|
||||
/* The address covered by the breakpoint must be included in
|
||||
[tb->pc, tb->pc + tb->size) in order to for it to be
|
||||
properly cleared -- thus we increment the PC here so that
|
||||
the logic setting tb->size below does the right thing. */
|
||||
ctx.base.pc_next += 4;
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DISAS("----------------\n");
|
||||
LOG_DISAS("nip=" TARGET_FMT_lx " super=%d ir=%d\n",
|
||||
ctx.base.pc_next, ctx.mem_idx, (int)msr_ir);
|
||||
if (ctx.base.num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) {
|
||||
gen_io_start();
|
||||
}
|
||||
if (unlikely(need_byteswap(&ctx))) {
|
||||
ctx.opcode = bswap32(cpu_ldl_code(env, ctx.base.pc_next));
|
||||
} else {
|
||||
ctx.opcode = cpu_ldl_code(env, ctx.base.pc_next);
|
||||
}
|
||||
LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n",
|
||||
ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
|
||||
opc3(ctx.opcode), opc4(ctx.opcode),
|
||||
ctx.le_mode ? "little" : "big");
|
||||
ctx.base.pc_next += 4;
|
||||
table = env->opcodes;
|
||||
handler = table[opc1(ctx.opcode)];
|
||||
LOG_DISAS("translate opcode %08x (%02x %02x %02x %02x) (%s)\n",
|
||||
ctx->opcode, opc1(ctx->opcode), opc2(ctx->opcode),
|
||||
opc3(ctx->opcode), opc4(ctx->opcode),
|
||||
ctx->le_mode ? "little" : "big");
|
||||
ctx->base.pc_next += 4;
|
||||
table = env->opcodes;
|
||||
handler = table[opc1(ctx->opcode)];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
handler = table[opc2(ctx->opcode)];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
handler = table[opc2(ctx.opcode)];
|
||||
handler = table[opc3(ctx->opcode)];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
handler = table[opc3(ctx.opcode)];
|
||||
if (is_indirect_opcode(handler)) {
|
||||
table = ind_table(handler);
|
||||
handler = table[opc4(ctx.opcode)];
|
||||
}
|
||||
handler = table[opc4(ctx->opcode)];
|
||||
}
|
||||
}
|
||||
/* Is opcode *REALLY* valid ? */
|
||||
if (unlikely(handler->handler == &gen_invalid)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: "
|
||||
"%02x - %02x - %02x - %02x (%08x) "
|
||||
TARGET_FMT_lx " %d\n",
|
||||
opc1(ctx.opcode), opc2(ctx.opcode),
|
||||
opc3(ctx.opcode), opc4(ctx.opcode),
|
||||
ctx.opcode, ctx.base.pc_next - 4, (int)msr_ir);
|
||||
} else {
|
||||
uint32_t inval;
|
||||
|
||||
if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE) && Rc(ctx.opcode))) {
|
||||
inval = handler->inval2;
|
||||
} else {
|
||||
inval = handler->inval1;
|
||||
}
|
||||
|
||||
if (unlikely((ctx.opcode & inval) != 0)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: "
|
||||
"%02x - %02x - %02x - %02x (%08x) "
|
||||
TARGET_FMT_lx "\n", ctx.opcode & inval,
|
||||
opc1(ctx.opcode), opc2(ctx.opcode),
|
||||
opc3(ctx.opcode), opc4(ctx.opcode),
|
||||
ctx.opcode, ctx.base.pc_next - 4);
|
||||
gen_inval_exception(ctxp, POWERPC_EXCP_INVAL_INVAL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
(*(handler->handler))(&ctx);
|
||||
#if defined(DO_PPC_STATISTICS)
|
||||
handler->count++;
|
||||
#endif
|
||||
/* Check trace mode exceptions */
|
||||
if (unlikely(ctx.singlestep_enabled & CPU_SINGLE_STEP &&
|
||||
(ctx.base.pc_next <= 0x100 || ctx.base.pc_next > 0xF00) &&
|
||||
ctx.exception != POWERPC_SYSCALL &&
|
||||
ctx.exception != POWERPC_EXCP_TRAP &&
|
||||
ctx.exception != POWERPC_EXCP_BRANCH)) {
|
||||
gen_exception_nip(ctxp, POWERPC_EXCP_TRACE, ctx.base.pc_next);
|
||||
} else if (unlikely(((ctx.base.pc_next & (TARGET_PAGE_SIZE - 1))
|
||||
== 0) ||
|
||||
(ctx.base.singlestep_enabled) ||
|
||||
singlestep ||
|
||||
ctx.base.num_insns >= max_insns)) {
|
||||
/* if we reach a page boundary or are single stepping, stop
|
||||
* generation
|
||||
*/
|
||||
break;
|
||||
}
|
||||
if (tcg_check_temp_count()) {
|
||||
fprintf(stderr, "Opcode %02x %02x %02x %02x (%08x) leaked "
|
||||
"temporaries\n", opc1(ctx.opcode), opc2(ctx.opcode),
|
||||
opc3(ctx.opcode), opc4(ctx.opcode), ctx.opcode);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (tb_cflags(tb) & CF_LAST_IO)
|
||||
gen_io_end();
|
||||
if (ctx.exception == POWERPC_EXCP_NONE) {
|
||||
gen_goto_tb(&ctx, 0, ctx.base.pc_next);
|
||||
} else if (ctx.exception != POWERPC_EXCP_BRANCH) {
|
||||
if (unlikely(ctx.base.singlestep_enabled)) {
|
||||
gen_debug_exception(ctxp);
|
||||
/* Is opcode *REALLY* valid ? */
|
||||
if (unlikely(handler->handler == &gen_invalid)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid/unsupported opcode: "
|
||||
"%02x - %02x - %02x - %02x (%08x) "
|
||||
TARGET_FMT_lx " %d\n",
|
||||
opc1(ctx->opcode), opc2(ctx->opcode),
|
||||
opc3(ctx->opcode), opc4(ctx->opcode),
|
||||
ctx->opcode, ctx->base.pc_next - 4, (int)msr_ir);
|
||||
} else {
|
||||
uint32_t inval;
|
||||
|
||||
if (unlikely(handler->type & (PPC_SPE | PPC_SPE_SINGLE | PPC_SPE_DOUBLE)
|
||||
&& Rc(ctx->opcode))) {
|
||||
inval = handler->inval2;
|
||||
} else {
|
||||
inval = handler->inval1;
|
||||
}
|
||||
|
||||
if (unlikely((ctx->opcode & inval) != 0)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "invalid bits: %08x for opcode: "
|
||||
"%02x - %02x - %02x - %02x (%08x) "
|
||||
TARGET_FMT_lx "\n", ctx->opcode & inval,
|
||||
opc1(ctx->opcode), opc2(ctx->opcode),
|
||||
opc3(ctx->opcode), opc4(ctx->opcode),
|
||||
ctx->opcode, ctx->base.pc_next - 4);
|
||||
gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL);
|
||||
ctx->base.is_jmp = DISAS_NORETURN;
|
||||
return;
|
||||
}
|
||||
}
|
||||
(*(handler->handler))(ctx);
|
||||
#if defined(DO_PPC_STATISTICS)
|
||||
handler->count++;
|
||||
#endif
|
||||
/* Check trace mode exceptions */
|
||||
if (unlikely(ctx->singlestep_enabled & CPU_SINGLE_STEP &&
|
||||
(ctx->base.pc_next <= 0x100 || ctx->base.pc_next > 0xF00) &&
|
||||
ctx->exception != POWERPC_SYSCALL &&
|
||||
ctx->exception != POWERPC_EXCP_TRAP &&
|
||||
ctx->exception != POWERPC_EXCP_BRANCH)) {
|
||||
gen_exception_nip(ctx, POWERPC_EXCP_TRACE, ctx->base.pc_next);
|
||||
}
|
||||
|
||||
if (tcg_check_temp_count()) {
|
||||
qemu_log("Opcode %02x %02x %02x %02x (%08x) leaked "
|
||||
"temporaries\n", opc1(ctx->opcode), opc2(ctx->opcode),
|
||||
opc3(ctx->opcode), opc4(ctx->opcode), ctx->opcode);
|
||||
}
|
||||
|
||||
ctx->base.is_jmp = ctx->exception == POWERPC_EXCP_NONE ?
|
||||
DISAS_NEXT : DISAS_NORETURN;
|
||||
}
|
||||
|
||||
static void ppc_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
DisasContext *ctx = container_of(dcbase, DisasContext, base);
|
||||
|
||||
if (ctx->exception == POWERPC_EXCP_NONE) {
|
||||
gen_goto_tb(ctx, 0, ctx->base.pc_next);
|
||||
} else if (ctx->exception != POWERPC_EXCP_BRANCH) {
|
||||
if (unlikely(ctx->base.singlestep_enabled)) {
|
||||
gen_debug_exception(ctx);
|
||||
}
|
||||
/* Generate the return instruction */
|
||||
tcg_gen_exit_tb(0);
|
||||
}
|
||||
gen_tb_end(tb, ctx.base.num_insns);
|
||||
}
|
||||
|
||||
tb->size = ctx.base.pc_next - ctx.base.pc_first;
|
||||
tb->icount = ctx.base.num_insns;
|
||||
static void ppc_tr_disas_log(const DisasContextBase *dcbase, CPUState *cs)
|
||||
{
|
||||
qemu_log("IN: %s\n", lookup_symbol(dcbase->pc_first));
|
||||
log_target_disas(cs, dcbase->pc_first, dcbase->tb->size);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_DISAS)
|
||||
if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
|
||||
&& qemu_log_in_addr_range(ctx.base.pc_first)) {
|
||||
qemu_log_lock();
|
||||
qemu_log("IN: %s\n", lookup_symbol(ctx.base.pc_first));
|
||||
log_target_disas(cs, ctx.base.pc_first,
|
||||
ctx.base.pc_next - ctx.base.pc_first);
|
||||
qemu_log("\n");
|
||||
qemu_log_unlock();
|
||||
}
|
||||
#endif
|
||||
static const TranslatorOps ppc_tr_ops = {
|
||||
.init_disas_context = ppc_tr_init_disas_context,
|
||||
.tb_start = ppc_tr_tb_start,
|
||||
.insn_start = ppc_tr_insn_start,
|
||||
.breakpoint_check = ppc_tr_breakpoint_check,
|
||||
.translate_insn = ppc_tr_translate_insn,
|
||||
.tb_stop = ppc_tr_tb_stop,
|
||||
.disas_log = ppc_tr_disas_log,
|
||||
};
|
||||
|
||||
void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
|
||||
{
|
||||
DisasContext ctx;
|
||||
|
||||
translator_loop(&ppc_tr_ops, &ctx.base, cs, tb);
|
||||
}
|
||||
|
||||
void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb,
|
||||
|
Loading…
Reference in New Issue
Block a user