QEMU中taget code翻译为TCG 中间码的过程

目录

1.TCG中间码的宏定义

2. 代码翻译过程(gen_intermediate_code函数的解析,这里以PPC体系架构为例)


1.TCG中间码的宏定义

  该头文件位于tcg/tcg-opc.h文件下,部分定义如下所示:

DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT)
DEF(movi_i32, 1, 0, 1, TCG_OPF_NOT_PRESENT)
DEF(setcond_i32, 1, 2, 1, 0)
DEF(movcond_i32, 1, 4, 1, IMPL(TCG_TARGET_HAS_movcond_i32))
/* load/store */
DEF(ld8u_i32, 1, 1, 1, 0)
DEF(ld8s_i32, 1, 1, 1, 0)
DEF(ld16u_i32, 1, 1, 1, 0)
DEF(ld16s_i32, 1, 1, 1, 0)
DEF(ld_i32, 1, 1, 1, 0)
DEF(st8_i32, 0, 2, 1, 0)
DEF(st16_i32, 0, 2, 1, 0)
DEF(st_i32, 0, 2, 1, 0)

 以上TCG微型操作码的宏定义位于tcg/tcg.h文件中line204,宏定义如下

typedef enum TCGOpcode {
#define DEF(name, oargs, iargs, cargs, flags) INDEX_op_ ## name,
#include "tcg-opc.h"
#undef DEF
    NB_OPS,
} TCGOpcode;

  最终生成的TCG微型操作码有INDEX_op_movi_i64、INDEX_op_ld8u_i64、INDEX_op_discard等还有一些qemu专用的如INDEX_op_insn_start、INDEX_op_exit_tb等。

2. 代码翻译过程(gen_intermediate_code函数的解析,这里以PPC体系架构为例)

   gen_intermediate_code函数定义位于target/ppc/traslate.c文件中,具体定义如下

void gen_intermediate_code(CPUState *cs, struct TranslationBlock *tb)
{
    DisasContext ctx;
    translator_loop(&ppc_tr_ops, &ctx.base, cs, tb);
}

    其中调用了translator_loop函数位于accel/tcg/translator.c文件下,函数具体定义如下:

void translator_loop(const TranslatorOps *ops, DisasContextBase *db,
                     CPUState *cpu, TranslationBlock *tb)
{
    int max_insns;

    /* Initialize DisasContext */
    db->tb = tb;
    db->pc_first = tb->pc;               //TB目标机中的第一条指令的地址
    db->pc_next = db->pc_first;          //pc_next是当前正在进行反汇编的指令的地址
    db->is_jmp = DISAS_NEXT;             //接下来要反汇编的指令类型
    db->num_insns = 0;                   //已经翻译的指令数
    db->singlestep_enabled = cpu->singlestep_enabled;

    /* Instruction counting */
    max_insns = tb_cflags(db->tb) & CF_COUNT_MASK;
    if (max_insns == 0) {
        max_insns = CF_COUNT_MASK;
    }
    if (max_insns > TCG_MAX_INSNS) {
        max_insns = TCG_MAX_INSNS;
    }
    if (db->singlestep_enabled || singlestep) {
        max_insns = 1;
    }

    max_insns = ops->init_disas_context(db, cpu, max_insns);
    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

    /* Reset the temp count so that we can identify leaks */
    tcg_clear_temp_count();

    /* Start translating.  开始翻译*/
    gen_tb_start(db->tb);
    ops->tb_start(db, cpu);     
    tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

    while (true) {
        db->num_insns++;
  ops->insn_start(db, cpu);//发布开始翻译的TCG操作码,即INDEX_op_insn_start操作码,并传递参数
        tcg_debug_assert(db->is_jmp == DISAS_NEXT);  /* no early exit */

        /* Pass breakpoint hits to target for further processing ,这部分不太懂*/
        if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) {
            CPUBreakpoint *bp;
            QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) {
                if (bp->pc == db->pc_next) {
                    if (ops->breakpoint_check(db, cpu, bp)) {
                        break;
                    }
                }
            }
            /* The breakpoint_check hook may use DISAS_TOO_MANY to indicate
               that only one more instruction is to be executed.  Otherwise
               it should use DISAS_NORETURN when generating an exception,
               but may use a DISAS_TARGET_* value for Something Else.  */
            if (db->is_jmp > DISAS_TOO_MANY) {
                break;
            }
        }

        /* Disassemble one instruction.  The translate_insn hook should
           update db->pc_next and db->is_jmp to indicate what should be
           done next -- either exiting this loop or locate the start of
           the next instruction.  */
        if (db->num_insns == max_insns && (tb_cflags(db->tb) & CF_LAST_IO)) {
            /* Accept I/O on the last instruction.  */
            gen_io_start();
            ops->translate_insn(db, cpu);
            gen_io_end();
        } else {
            ops->translate_insn(db, cpu);//将target code翻译为TCG code的函数,该函数定义位于target/ppc/traslate.c文件中line7307
        }

        /* Stop translation if translate_insn so indicated.  */
     if (db->is_jmp != DISAS_NEXT) {   //如果下一条要翻译的指令类型不是DISAS_NEXT跳出翻译循环
            break;
        }
        /* Stop translation if the output buffer is full,
           or we have executed all of the allowed instructions. 
           如果翻译的指令条数大于最大条数,退出翻译 */
        if (tcg_op_buf_full() || db->num_insns >= max_insns) {
            db->is_jmp = DISAS_TOO_MANY;
            break;
        }
    }

    /* Emit code to exit the TB, as indicated by db->is_jmp.  */
    ops->tb_stop(db, cpu);       /*依靠db->is_jmp发送INDEX_op_exit_tb退出TB*/
    gen_tb_end(db->tb, db->num_insns);

    /* The disas_log hook may use these values rather than recompute.  */
    db->tb->size = db->pc_next - db->pc_first;
    db->tb->icount = db->num_insns;

#ifdef DEBUG_DISAS
    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)
        && qemu_log_in_addr_range(db->pc_first)) {
        qemu_log_lock();
        qemu_log("----------------\n");
        ops->disas_log(db, cpu);
        qemu_log("\n");
        qemu_log_unlock();
    }
#endif
}

重点来看具体翻译的函数translate_insn======>ppc_tr_translate_insn函数

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);
    }
    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结构体中*/
    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[opc3(ctx->opcode)];
            if (is_indirect_opcode(handler)) {
                table = ind_table(handler);
                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(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;
}

 

你可能感兴趣的:(qemu学习)