【问题标题】:Where to store code constants when writing a JIT compiler? [closed]编写 JIT 编译器时在哪里存储代码常量? [关闭]
【发布时间】:2014-07-20 12:18:30
【问题描述】:

我正在为 x86-64 编写 JIT 编译器,我有一个关于将常量包含到我生成的机器代码中的最佳实践的问题。

到目前为止,我的方法很简单:

  • VirtualAllocmmap分配一块RW内存
  • 将机器代码加载到所述内存区域中。
  • VirtualProtectmprotect 标记页面可执行文件(并删除写权限以确保安全)。
  • 执行。

当我生成代码时,我必须包含常量(数字、字符串),我不确定什么是最好的方法。我想到了几种方法:

  • 将所有常量作为立即数存储到指令的操作码中。这对于除了小的标量值之外的所有东西来说似乎都是个坏主意。
  • 为常量分配一个单独的内存区域。在我看来,这似乎是最好的主意,但它会使内存管理和编译工作流程稍微复杂化 - 在开始编写可执行代码之前,我必须知道内存位置。此外,我不确定这是否会由于更差的内存局部性而以某种方式影响性能。
  • 将常量存储在与代码相同的区域,并使用 RIP 相对寻址对其进行访问。我喜欢这种方法,因为它将程序的相关部分放在一起,但我对混合指令和数据感到有些不安。
  • 完全不同的东西?

最好的方法是什么?

【问题讨论】:

  • 对于所有适合寄存器的常量,很明显它们应该是立即数。这就是立即数的用途。我不知道您所说的“小”是什么意思,除非您正在考虑多精度数字。不适合立即操作数的常量的单独内存页是一种有效的方法。
  • 对于机器指令集中支持作为立即值的所有常量,将它们放入指令中。
  • 我主要考虑的是字节字符串和打包的 SSE 浮点数。
  • HotSpot 使用 #1 和 #3,尽管我可能误解了 #1。我的意思是,如果您可以直接在指令中编码一个常量,这通常比从内存中加载它然后使用它更有效 - 也降低了寄存器压力。对于所有不起作用的常量,HotSpot 在代码之前 存储它们并在生成任何代码之前计算该区域的大小.. 避免所有的回补。优点?如果您以后必须移动代码,则无需修补任何内容,而且效率很高。

标签: c++ assembly x86-64 jit


【解决方案1】:

很大程度上取决于您生成二进制代码的方式。如果您使用处理标签和计算偏移量的 JIT 汇编器,事情就很简单了。您可以在代码结束后将常量粘贴在一个块中,使用对这些标签的 pc 相对引用,并最终得到一个包含代码和常量的字节块(易于管理)。如果您试图动态生成二进制代码,您已经遇到了如何处理前向 pc 相对引用(例如,对于前向分支)的问题。如果您使用回补丁,则需要对其进行扩展以支持对常量块的引用。

您可以通过将常量放在单独的块中并将该块的地址作为参数传递给您的代码来避免 pc 相对偏移量计算。这几乎就是您建议的“为常量分配一个单独的区域”。如果将块作为参数传递,则无需知道块的地址。

【讨论】:

  • 您可以将常量放在代码之前,HotSpot 就是这样做的。唯一的要求是在生成代码之前检查常量一次以确定必要的大小。更简单,性能更轻巧。
猜你喜欢
  • 2015-03-23
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-28
  • 2011-06-29
相关资源
最近更新 更多