Skip to the content.

论文中涉及的问题

1. xqm 的寄存器分配算法

部分虚拟寄存器已经分配了物理寄存器。新的 tb 会首先遍历已经分配了的寄存器,如果没有空闲的物理寄存器可分配,就将该 tb 剩余的 itemp reg 全遍历一遍,放到内存指定区域,记录 virtual id。之后遍历已经分配了物理寄存器的虚拟寄存器(最多 6 个),如果这 6 个寄存器在该 tb 中没有用过,则为 dead virtual id,需要清除。清除后再将其分配给当前需要分配的 virtual id。遍历已经分配过的虚拟寄存器在该 tb 中是否出现过是为了不出现 reg 抖动。

存在的问题:

那如果已经分配过的虚拟寄存器在该 tb 中都出现过,就不能空出寄存器槽,岂不是分配失败?

可行的分配方法:

在 host 中采用硬件直接映射的方法,将 target 中常用的 3-4 个寄存器直接用 host 中的寄存器代替,然后再采用一般的分配算法。

没有应用超级块。tb-link 优化项应该是使得如果跳转指令的目标块已经翻译了,那么就不需要返回翻译,而是直接跳转到对应的目标块开始执行(待验证,需要看汇编之后的 mips 指令)。这个属于跳转指令的简单优化。

xqm 首先执行一个查找过程,查看代码是否再 code_cache 中,如果存在,就直接执行。如果已经连成链,就不用进行上下文切换,继续在 code_cache 中执行。如果没有连接成块,就检查是否能够构成链,如果能则构造,如果不能就在执行完当前块后进行上下文切换进入翻译模块,翻译下一个块(这个过程还没有验证)。

3. xqm 的上下文切换

prologue 用于保护 QEMU 的断点信息,恢复目的平台指令的断点信息;epilogue 用于保护目的平台指令的断点信息,恢复 QEMU 的断点信息。

为了在切换过程中保存和恢复 QEMU 使用的寄存器,每次切换均通过预先生成的 prologue 和 epilogue 代码进行过渡。每次要执行一个基本块时,首先跳转到一个预先生成的 prologue 缓冲区,在其中的语句为:

​ • 保存 s1-s7、gp、fp、ra,这是 QEMU、MIPS 均使用的寄存器。

​ • 跳转到基本块中首条 MIPS 指令的地址。

之后模拟器是在翻译后的目标地址中执行,直到执行到基本块的结束指令 exit_tb。exit_tb 对应的目标平台功能为:跳转到一个预先生成的 epilogue 缓冲区。epilogue 缓冲区中的语句为:

​ • 恢复 s1-s7、gp、fp、ra。

​ • 跳转回当初进入 prologue 的位置。

为什么执行完 generate_context_switch_native_to_bt()和 generate_context_switch_bt_to_native()之后都会返回到 translate.c:417 tr_ir2_assemble().

4. xqm 中有动静结合的翻译策略么,如果没有要怎么实现呢

没有动静结合框架。

5. xqm 中的 code_cache 管理

xqm 中没有特别的 code_cache 管理部分,ir1, ir2 都是存储在通过动态内存分配函数分配的内存中。其中 ir1 是通过 translate.c:327 mm_calloc()函数保存的, ir2 是通过 ir2.c:2603 ir2_append()函数保存的。而总的内存空间是通过 translate-all.c:1722 对 tb_alloc()函数的调用获取的。

QEMU 使用一段内存保存所有翻译后的指令块,所有翻译后的基本块组成一个哈希表,以基本块对应的首条目标平台指令的物理地址作为检索键值。QEMU 中还有一个较小的指令 cache 用于支持快速查找。实际上是每个链最多只有一个节点的 hash 表。cache 保存最近使用的基本块首地址。在针对一个指令地址查找基本块时,采用两级查找:首先在 cache、其次在 hash 中查找,当设定的内存被翻译后的指令块填满后,将整段内存清空,并装入新的指令块。

可行的管理算法:

LRC 策略把代码 Cache 按照所存储的本地码的执行热度分成上下两级,大小的比例是 1:4。上级 Cache 存储热度较高的本地码,本地码首先存入下级 Cache,达到一定热度后会被提升到上级。下级本地码被替换时会从 Cache 中替换出去,而上级本地码被替换时被换到下级 Cache 中。

6. xqm 中有 profiling 么,没有要怎么实现

有实现 profiling,不过是基于块的 profiling,还没有找到怎么怎么用 profile 信息。在 syscall.c:9258 调用 x86tomips_fini()函数,这个应该是根据 profile 优化,用 gdb 调试看,需要搞懂的有两点:怎样对收集到的信息进行处理(是否有生成超级块)和怎样利用这些信息优化。

7. 考虑这样一种情况,如果程序所需的库函数在本地没有怎么办

所以动态二进制翻译的库函数问题最重要的还是解决库的移植问题。

可行的方法:

在读取完.dynamic 信息,获得动态链接的有关信息。如果引用的库本地有可以用的替代库或该库已经翻译过,则直接加载引用;如果该库本地没有,则联网查找下载并用二进制翻译器翻译,然后保存到本地,再引用。还可以将翻译好的库上传到公司服务器,视其引用的频率决定是否将其加载到发行版的二进制翻译器中。

8. 传统的超级块构建是怎么样的

应该是动静结合,静态的构建超级块,优化跳转指令等,那对性能影响应该不大。考虑到实现很麻烦,将超级块的构建、分析、链接用子线程方法来完成总的来说没有动静结合可行性高。

9. 多线程实现

多线程执行的思路可以认真考虑以下,结合流水线的思想:一个线程反汇编,一个线程翻译,一个线程编译,然后优化是静态的。

10.准确的信息统计系统

应该设计准确的信息统计系统,用来统计执行情况,从而根据这些执行情况指导优化方向。统计系统应该统计但不限于这些信息:target 中寄存器的使用情况、不同模块的执行时间、每种指令执行所占时间比执行次数、每个模块的执行时间比、基本块的执行频率、code_cache 的命中率、分支跳转的情况。

11. 疑惑

《内核级二进制翻译系统设计及性能优化》一文(zotero 论文库中有)中设计的翻译系统和现在接触的 xqm 不一样,它解决的问题是需要用到部分内核模块,如驱动等的程序,但传统的应用级翻译系统无法直接翻译执行 OS 内核中的模块,只有系统级翻译系统才能做到。而实现系统级翻译器很复杂,故将翻译器的一部分实现在 target 内核,这可以理解为“半虚拟化”,而且这种做法只有自己开发 OS 才能实现。问题是现在没有接触过系统级翻译器,不能完全理解这种设计。而且虽然知道学过 OS,知道 OS 有不同的执行模式,但这都是概念上的认识,缺乏实际的认识。要如何加强系统方面的了解。

12. 间接分支的优化算法

挺好的:为每条间接分支保存几条预测的目标指令,通过哈希数组实现,查找方便,命中率高,能够减少上下文切换次数。实现起来并不复杂。

13. 多线程动态二进制翻译系统

将反汇编、翻译和执行分成不同的线程实现,类似于流水线,然后还加上预测翻译线程。能充分利用多核 CPU 优势,但多线程编程是个问题。