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