指令前端翻译
QEMU的前端翻译使用中间码实现guest指令的逻辑,中间码其实已经是一组完备的类RISC
汇编指令定义。我们做前端翻译,可以理解为使用中间码作为汇编语言实现guest指令的逻辑。
QEMU代码qemu/tcg/README有怎么使用中间码的完整描述,值得仔细学习下。
中间码的定义里有各种指令,还有不同作用域的“寄存器”的定义。我们关注的是各种“寄存器”,
也就是tcg变量的定义。
QEMU翻译基于TB,一个TB里又可能有多个basic block,我们叫BB,tcg变量的作用域和BB
有关系。tcg有三种类型的变量:temporary, local temporary和global,global这个不用
多说,一般系统寄存器是global变量,temporary变量的生命只在一个BB内,local temporary
变量的生命在一个TB内,可以跨越BB。
BB从上一个BB的结尾或者一个set_label指令开始, BB以分支指令(brcond_xxx)、goto_tb
以及exit_tb结束,
举一个arm里的例子:target/arm/translate.c
1 | static bool trans_LE |
这里的decr就要是一个local temporary,因为这个变量在brcon_i32的前后都要使用,已经
跨越了两个BB,这里的decr换成temporary变量就会出错。
global变量又分为寄存器和memory两类。memory一般定义的是CPU里的寄存器,比如riscv
里是这样定义pc寄存器:
1 | cpu_pc = tcg_global_mem_new(cpu_env, offsetof(CPURISCVState, pc), "pc"); |
这种TCGv在后端翻译时,会把guest CPU寄存器的值先load到host寄存器里,计算完后再store
回guest CPU结构体里,模拟过程会有访存行为。
寄存器这种TCGv在后端翻译时,会直接映射到host的寄存器上,每次就可以直接访问,一般
用来存放guest CPU env的指针,比如riscv上env变量是这么定义的:
1 | /* tcg/tcg.c */ |
QEMU代码分析
代码分析需要涉及tcg后端翻译,具体代码分析可以参考这里。