目录
1.TCG中间码的宏定义
2. 代码翻译过程(gen_intermediate_code函数的解析,这里以PPC体系架构为例)
该头文件位于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等。
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;
}