【发布时间】:2014-07-20 12:18:30
【问题描述】:
我正在为 x86-64 编写 JIT 编译器,我有一个关于将常量包含到我生成的机器代码中的最佳实践的问题。
到目前为止,我的方法很简单:
- 用
VirtualAlloc或mmap分配一块RW内存 - 将机器代码加载到所述内存区域中。
- 用
VirtualProtect或mprotect标记页面可执行文件(并删除写权限以确保安全)。 - 执行。
当我生成代码时,我必须包含常量(数字、字符串),我不确定什么是最好的方法。我想到了几种方法:
- 将所有常量作为立即数存储到指令的操作码中。这对于除了小的标量值之外的所有东西来说似乎都是个坏主意。
- 为常量分配一个单独的内存区域。在我看来,这似乎是最好的主意,但它会使内存管理和编译工作流程稍微复杂化 - 在开始编写可执行代码之前,我必须知道内存位置。此外,我不确定这是否会由于更差的内存局部性而以某种方式影响性能。
- 将常量存储在与代码相同的区域,并使用 RIP 相对寻址对其进行访问。我喜欢这种方法,因为它将程序的相关部分放在一起,但我对混合指令和数据感到有些不安。
- 完全不同的东西?
最好的方法是什么?
【问题讨论】:
-
对于所有适合寄存器的常量,很明显它们应该是立即数。这就是立即数的用途。我不知道您所说的“小”是什么意思,除非您正在考虑多精度数字。不适合立即操作数的常量的单独内存页是一种有效的方法。
-
对于机器指令集中支持作为立即值的所有常量,将它们放入指令中。
-
我主要考虑的是字节字符串和打包的 SSE 浮点数。
-
HotSpot 使用 #1 和 #3,尽管我可能误解了 #1。我的意思是,如果您可以直接在指令中编码一个常量,这通常比从内存中加载它然后使用它更有效 - 也降低了寄存器压力。对于所有不起作用的常量,HotSpot 在代码之前 存储它们并在生成任何代码之前计算该区域的大小.. 避免所有的回补。优点?如果您以后必须移动代码,则无需修补任何内容,而且效率很高。