[hellogcc] Re: [投稿] QEMU Internal - Block Chaining 1/3

  • From: Yao Qi <qiyaoltc@xxxxxxxxx>
  • To: hellogcc@xxxxxxxxxxxxx
  • Date: Wed, 5 Oct 2011 11:13:43 +0800

On Mon, Oct 03, 2011 at 12:00:00PM +0800, 陳韋任 wrote:
> > > translation block 執行完後,它的跳躍目標確定且該跳躍目標也已經
> > > 在 code cache 裡,那我們就把這兩個 translation block 串接起來。
> > > 這個就叫做 block chaining/linking。
> > > 
> > 
> > 这个在qemu是能控制的吗? block chaining/linking 可以打开或者关闭吗?
> 
>   是可以控制的。:-)
> 
>   QEMU 在生成 TCG IR 的時候,有考慮到 block chaining。所以在之後,一但
> 確定 tb1 下一個 block,tb2,在 code cache 的位址,QEMU 就會 patch 在
> code cache 中的 tb1 其 jmp 指令的 address,這樣 tb1 一執行完就會直接跳
> 到 tb2 執行。
> 
>   struct TranslationBlock 裡的欄位 jmp_first 和 jmp_next 就是用來記錄整個
> 在 code cache 中 block chaining 的情況,這是用來以後做 block unchaining
> 之用。也就是你所說的關閉。
> 
>   我之後再來講 block unchaining。:-)

好的,我们可以在后边的文章里边介绍。

>  
> > > // 一般是這樣呼叫: tcg_gen_exit_tb((tcg_target_long)tb + tb_num);
> > > // 注意! 請留意其參數: (tcg_target_long)tb + tb_num。
> > > static inline void tcg_gen_exit_tb(tcg_target_long val)
> > > {
> > >     // 將 INDEX_op_exit_tb 寫入 gen_opc_buf; val 寫入 gen_opparam_buf。
> > >     tcg_gen_op1i(INDEX_op_exit_tb, val);
> > > }
> > > 
> > 
> > 不管是否支持block chaining/linking,这里应该都是这样吧。
> 
>   不太能這麼說。你看一下 cpu_exec 內層迴圈。
> 
>     if (next_tb != 0 && tb->page_addr[1] == -1) {
>         // 這邊利用 TranlationBlock 指針的最低有效位後兩位指引 block
>         // chaining 的方向。
>         // next_tb -> tb
>         tb_add_jump((TranslationBlock *)(next_tb & ~3), next_tb & 3, tb);
>     }
> 
>     // 這邊要注意到,QEMU 利用 TranslatonBlock 指針後兩位必為零的結果
>     // 做了一些手腳。QEMU 將其末兩位編碼成 0、1 或 2 來指引 block chaing
>     // 的方向。這種技巧在 QEMU 用得非常嫻熟。
>     next_tb = tcg_qemu_tb_exec(tc_ptr);
> 
>   tcg_gen_exit_tb 的參數 val,就是 tcg_qemu_tb_exec 的返回值 next_tb。這
> val 是給 block chaining 用的,看一下 tb_add_jump。
> 
>   如果不做 block chaining,那直接返回 QEMU 就好,應該不需要返回什麼參數。
> 只需在返回之前,更新 env->eip。這樣從 code cache 回到 QEMU 之後,tb_find_fast
> 就會看 env->eip 是否已被翻譯過。
> 

哦,谢谢解释。

> > >     case INDEX_op_exit_tb:
> > >         // QEMU 把跳至 code cache 執行當作是函式呼叫,EAX 存放返回值。
> > >         // 將 val 寫進 EAX,val 是 (tcg_target_long)tb + tb_num。 
> > >         tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_EAX, args[0]);
> > > 
> > 
> > 这里相当于生成了这样一条指令 mov eax, #(tcg_target_long)tb + tb_num ?
> 
>   沒錯。
>  
> > >         // e9 是 jmp 指令,後面的 operand 為相對偏移量,將會加上 eip。
> > >         // 底下兩條的總和效果是跳回 code_gen_prologue 中 prologue 以後的位置。
> > >         tcg_out8(s, 0xe9); /* jmp tb_ret_addr */
> > 
> > 这里可以多介绍一下jmp指令,可能跟好理解。jmp指令一共五个字节,第一个字节是opcode,
> > 应该就是 e9,这里生成一个字节的opcode
> > 

你能把这里具体生成什么host 汇编指令,也明显的列出来吗?比如这里就是mov和jmp
两条指令。这样文章更好理解一些。

> > >         // tb_ret_addr 在 tcg_target_qemu_prologue 初始成指向 code_gen_prologue
> > >         // 中 prologue 以後的位置。
> > >         // 生成 host binary 的同時,s->code_ptr 會移向下一個 code buffer 的位址。
> > >         // 所以要減去 4。
> > >         tcg_out32(s, tb_ret_addr - s->code_ptr - 4);
> > 
> > 然后生成跳转地址偏移 tb_ret_addr - s->code_ptr - 4,所以就是这样一条指令
> > 
> >   jmp (tb_ret_addr - s->code_ptr - 4)
> > 
> > 这个 - 4 我不是和明白,是因为上边那条 mov eax, #((tcg_target_long)tb +
> > tb_num),所以 - 4 吗?
>  
>   說實在話,我對 x86 ISA 不很熟,慚愧。我想是生成 host binary 的同時,s->code_ptr會
> 移向下一個 code buffer 的位址,所以要減去 4。
>  

哦,这里就算了吧。

> > 这里想到于生成了这样的两个指令
> > 
> > 
> >    mov eax, #(tcg_target_long)tb + tb_num
> >    jmp (tb_ret_addr - s->code_ptr - 4)
> > 
> > jmp过去以后,eax的值有什么用吗? 
> 
>   QEMU 將跳至 code cache 執行視為一個函式呼叫。code cache 是 function
> body。那 function prologue/epilogue 在哪? 在 code_gen_prologue 裡面,
> 你可以回去參考一下 QEMU Internal - Tiny Code Generator (TCG) 1/2。
> code cache -> code_gen_prologue -> QEMU (tcg_qemu_tb_exec)。我想那邊
> 就是把 eax 上的內容當作返回值。
> 

哦,谢谢。对于我们自己也不是很确定的,我们还是不要说了,免得confuse别人
:) 。你这样就很好。
 
这个系列的文章也可以贴到hellogcc博客了。贴好了,我们好宣传宣传。 :)

-- 
Yao Qi <qiyaoltc AT gmail DOT com>

To be or not to be.
                -- Shakespeare
To do is to be.
                -- Nietzsche
To be is to do.
                -- Sartre
Do be do be do.
                -- Sinatra

Other related posts: