QEMU: TCG Translation

QEMU uses Tiny Code Generator to translate code from target architecture to host architecture. One target instruction may be translated into many TCG instructions. It is the same concept as process virtual machine.

Target code is processed one instruction by one instruction to extract an instruction and operands out of each Program Counter address. It is the task for porters to craft an equivalent set of TCG instructions for a target instruction. You can see all available front-end TCG instructions in QEMU Wikipedia or in tcg/tcg-op.h.

In this post, I will give a part of lm32‘s branch equal instruction translation example. QEMU version is 2.0. I assume a host machine using i386 architecture.

We start from going to target-lm32/translate.c first. It is a common practice in QEMU putting all TCG front-end translation for an architecture into this translate.c (I will talk about helper function in a later post). The function for translating branch equal is named “dec_be“.

static void dec_be(DisasContext *dc) {
LOG_DIS("be r%d, r%d, %d\n", dc->r0, dc->r1, sign_extend(dc->imm16, 16) * 4);
gen_cond_branch(dc, TCG_COND_EQ);
}

It calls another function named “gen_cond_branch()” in the same file.

static inline void gen_cond_branch(DisasContext *dc, int cond)
{
    int l1; 
    l1 = gen_new_label();
    tcg_gen_brcond_tl(cond, cpu_R[dc->r0], cpu_R[dc->r1], l1); 
    gen_goto_tb(dc, 0, dc->pc + 4);
    gen_set_label(l1);
    gen_goto_tb(dc, 1, dc->;pc + (sign_extend(dc->imm16 << 2, 16)));
    dc->is_jmp = DISAS_TB_JUMP;
}

Now, it asked TCG to generate TCG code for it. It called “tcg_gen_brcond_tl()“. This function is substituted by the macro in tcg/tcg-op.h.

#define tcg_gen_brcond_tl tcg_gen_brcond_i32

It redirects to “tcg_gen_brcond_i32()“.

static inline void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, int label_index)
{
    if (cond == TCG_COND_ALWAYS) {
        tcg_gen_br(label_index);
    } else if (cond != TCG_COND_NEVER) {
        tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_index); 
    }
}

It calls “tcg_gen_op4ii_i32()“.

static inline void tcg_gen_op4ii_i32(TCGOpcode opc, TCGv_i32 arg1, TCGv_i32 arg2, TCGArg arg3, TCGArg arg4)
{
    *tcg_ctx.gen_opc_ptr++ = opc;
    *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg1);
    *tcg_ctx.gen_opparam_ptr++ = GET_TCGV_I32(arg2);
    *tcg_ctx.gen_opparam_ptr++ = arg3;
    *tcg_ctx.gen_opparam_ptr++ = arg4;
}

This is the final destination for our front-end translation.

Translation from target code to TCG code stacks up 2 array variables defined in tcg/tcg.h.

  1. gen_opc_buf – instruction buffer / pointed by gen_opc_ptr
  2. gen_opparam_buf – operand buffer / pointed by gen_opparam_ptr

If you look back to “tcg_gen_op4ii_i32(INDEX_op_brcond_i32, …);“. This INDEX_op_brcond_i32 is the link between front-end and back-end of TCG. It is TCG instruction! We finally found it.

After this point, our TCG instruction will be translated into host instructions. The function named “tcg_out_op()” in tcg/i386/tcg-target.c translates into host code. If you run QEMU on other architecture, such as, PowerPC or MIPS, the file location is changed to tcg/ppc/tcg-target.c and tcg/mips/tcg-target.c, respectively.

static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, const int *const_args)
{
    …
    case INDEX_op_brcond_i32:
        tcg_out_brcond32(s, args[2], args[0], args[1], const_args[1], args[3], 0); 
        break; 
    …
}

It expands into several functions.

static void tcg_out_brcond32(TCGContext *s, TCGCond cond,
                       TCGArg arg1, TCGArg arg2, int const_arg2,
                       int label_index, int small) {
    tcg_out_cmp(s, arg1, arg2, const_arg2, 0); 
    tcg_out_jxx(s, tcg_cond_to_jcc[cond], label_index, small);
}

So, one TCG conditional branch instruction means 2 i386 instructions: compare (cmp) and conditional jump (jcc).

static void tcg_out_cmp(TCGContext *s, TCGArg arg1, TCGArg arg2, int const_arg2, int rexw)
{
    if (const_arg2) {
        if (arg2 == 0) {
        /* test r, r */
            tcg_out_modrm(s, OPC_TESTL + rexw, arg1, arg1); 
        else {
            tgen_arithi(s, ARITH_CMP + rexw, arg1, arg2, 0);
        }
    } else {
        tgen_arithr(s, ARITH_CMP + rexw, arg1, arg2);
    }
}
static void tcg_out_modrm(TCGContext *s, int opc, int r, int rm) {
    tcg_out_opc(s, opc, r, rm, 0);
    tcg_out8(s, 0xc0 | (LOWREGMASK(r) << 3) | LOWREGMASK(rm));
}
static void tcg_out_opc(TCGContext *s, int opc)
{
  if (opc & P_DATA16) {
      tcg_out8(s, 0x66);
  }
  if (opc & (P_EXT | P_EXT38)) {
      tcg_out8(s, 0x0f);
      if (opc & P_EXT38) {
          tcg_out8(s, 0x38);
      }
  }
  tcg_out8(s, opc); 
static inline void tcg_out8(TCGContext *s, uint8_t v) {
    *s->code_ptr++ = v; 
}

If you follow along bold font statements, you can see that OPC_TESTL from i386 has finally landed in a host code area. code_ptr points to gen_code_buf array.


edit1: gen_opparam_buf – operand buffer / pointed by gen_opparam_ptr gen_opparam_buf (Thank P.C)

 

Advertisements

2 thoughts on “QEMU: TCG Translation

  1. HI , just want to kindly point out a typo.
    “gen_opparam_buf – operand buffer / pointed by gen_opparam_buf”
    should be “gen_opparam_ptr”, right?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s