Commit b0c2d521 authored by Emilio G. Cota's avatar Emilio G. Cota Committed by David Gibson
Browse files

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: default avatarRichard Henderson <richard.henderson@linaro.org>
Signed-off-by: default avatarEmilio G. Cota <cota@braap.org>
Signed-off-by: default avatarDavid Gibson <david@gibson.dropbear.id.au>
parent b6bac4bc
Loading
Loading
Loading
Loading
+167 −162
Original line number Diff line number Diff line
@@ -7207,131 +7207,130 @@ 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;

    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;
    int bound;

    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);
}
    if (max_insns > TCG_MAX_INSNS) {
        max_insns = TCG_MAX_INSNS;

static void ppc_tr_tb_start(DisasContextBase *db, CPUState *cs)
{
}

    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++;
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);

        if (unlikely(cpu_breakpoint_test(cs, ctx.base.pc_next, BP_ANY))) {
            gen_debug_exception(ctxp);
    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;
            break;
    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 (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));
              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);
        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;
              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)];
    handler = table[opc1(ctx->opcode)];
    if (is_indirect_opcode(handler)) {
        table = ind_table(handler);
            handler = table[opc2(ctx.opcode)];
        handler = table[opc2(ctx->opcode)];
        if (is_indirect_opcode(handler)) {
            table = ind_table(handler);
                handler = table[opc3(ctx.opcode)];
            handler = table[opc3(ctx->opcode)];
            if (is_indirect_opcode(handler)) {
                table = ind_table(handler);
                    handler = table[opc4(ctx.opcode)];
                handler = table[opc4(ctx->opcode)];
            }
        }
    }
@@ -7340,84 +7339,90 @@ void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
        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);
                      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))) {
        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)) {
        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;
                          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);
    (*(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 (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()) {
            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);
        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;
}
    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);

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;
}

#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();
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);
}
#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,