C 在汇编语言中编写JIT编译器
我已经用C编写了一个虚拟机,对于非JIT虚拟机来说,它的性能相当不错,但我想学习一些新的东西,并提高性能。我当前的实现只是使用一个开关将VM字节码转换为指令,指令被编译成跳转表。正如我所说,它的性能相当不错,但我遇到了一个只有JIT编译器才能克服的障碍 不久前我已经问过一个类似的问题,关于自我修改代码,但我意识到我问的问题不对 所以我的目标是为这个C虚拟机编写一个JIT编译器,我想在x86汇编中实现它。(我使用NASM作为我的汇编器)我不太确定该如何进行这项工作。我对汇编很熟悉,我已经看过一些自我修改的代码示例,但我还没有弄清楚如何生成代码 到目前为止,我的主要任务是用我的参数将指令复制到一块可执行内存中。我知道我可以在NASM中标记某一行,并使用静态参数从该地址复制整行,但这不是非常动态的,并且不适用于JIT编译器。我需要能够从字节码解释指令,将其复制到可执行内存,解释第一个参数,将其复制到内存,然后解释第二个参数,并将其复制到内存 我已经被告知了一些可以使这项任务更容易的库,比如GNULightning,甚至LLVM。然而,在使用外部资源之前,我想先手写这篇文章,以了解它是如何工作的C 在汇编语言中编写JIT编译器,c,assembly,code-generation,jit,self-modifying,C,Assembly,Code Generation,Jit,Self Modifying,我已经用C编写了一个虚拟机,对于非JIT虚拟机来说,它的性能相当不错,但我想学习一些新的东西,并提高性能。我当前的实现只是使用一个开关将VM字节码转换为指令,指令被编译成跳转表。正如我所说,它的性能相当不错,但我遇到了一个只有JIT编译器才能克服的障碍 不久前我已经问过一个类似的问题,关于自我修改代码,但我意识到我问的问题不对 所以我的目标是为这个C虚拟机编写一个JIT编译器,我想在x86汇编中实现它。(我使用NASM作为我的汇编器)我不太确定该如何进行这项工作。我对汇编很熟悉,我已经看过一些自
这个社区有什么资源或例子可以帮助我开始这项任务吗?举一个简单的例子,展示两个或三个指令,比如“add”和“mov”,用来生成可执行代码,并在内存中动态地使用参数,这会很奇妙。我不建议在汇编中编写JIT。在汇编中编写解释器最频繁执行的位有很好的理由。如需了解其外观的示例,请参见LuaJIT的作者 至于JIT,有许多不同的级别,其复杂性各不相同:
; ebp points to virtual register 0 on the stack
instr_ADD:
<decode instruction>
mov eax, [ebp + ecx * 4] ; load first operand from stack
add eax, [ebp + edx * 4] ; add second operand from stack
mov [ebp + ebx * 4], eax ; write back result
<dispatch next instruction>
instr_SUB:
... ; similar
这只是复制相关代码,因此我们需要相应地初始化所使用的寄存器。更好的解决方案是将其直接转换为机器指令mov eax,[ebp+4]
,但现在您已经需要手动编码请求的指令
这种技术消除了解释的开销,但在其他方面并不能大大提高效率。如果代码只执行一到两次,那么首先将其转换为机器代码(至少需要刷新部分I-cache)可能不值得我建议你看看这个项目。通过使用它提供的框架,您可以节省大量能源。如果你想手工编写所有东西,只要阅读源代码并自己重写就行了,我认为这并不难。仅仅因为抖动生成机器代码并不意味着它本身就需要在汇编中编写。这样做没有任何意义。尝试的中间步骤是使用GCC的计算goto扩展(使用
void*optable[]={&&op\u add,&&op\u subtract,…}
进行线程分派,每个操作数都是op\u add:…goto*optable[*ip++];
)。我已经看到了你所描述的转换口译员的巨大进步。对,在每个基本模块的末尾,你必须回到一个决定分支位置的例程。这将产生一个新的字节码地址,该地址必须映射到相应机器码的地址。有一种称为JIT的技术,允许更平滑地集成解释器和JIT。其主要思想是将分支转换为实际的机器指令,以便分支预测器可以看到它们。提供的url不起作用
mov ecx, 1
mov edx, 2
mov ebx, 3
mov eax, [ebp + ecx * 4] ; load first operand from stack
add eax, [ebp + edx * 4] ; add second operand from stack
mov [ebp + ebx * 4], eax ; write back result
mov ecx, 3
mov edx, 4
mov ebx, 3
mov eax, [ebp + ecx * 4] ; load first operand from stack
sub eax, [ebp + edx * 4] ; add second operand from stack
mov [ebp + ebx * 4], eax ; write back result