关于解释器:JIT代码生成技术

关于解释器:JIT代码生成技术

JIT code generation techniques

虚拟机如何动态生成本机代码并执行它?

假设您可以弄清楚您要发出的本机操作码是什么,如何实际运行它?

是否像将助记符指令映射到二进制代码,将其填充到char *指针并将其强制转换为函数并执行一样,有点怪癖?

还是会生成一个临时共享库(.dll或.so或其他),然后使用LoadLibrary之类的标准函数将其加载到内存中?


您可以使程序计数器指向您要执行的代码。请记住,数据可以是数据或代码。在x86上,程序计数器是EIP寄存器。 EIP的IP部分代表指令指针。调用JMP指令以跳转到一个地址。跳转后,EIP将包含此地址。

Is it something as hacky as mapping the mnemonic instructions to binary codes, stuffing it into an char* pointer and casting it as a function and executing?

是。这是一种方法。结果代码将转换为C语言中的函数指针。


Is it something as hacky as mapping the mnemonic instructions to binary codes, stuffing it into an char* pointer and casting it as a function and executing?

是的,如果您使用C或C ++(或类似的东西)来做,那正是您要做的。

看起来很黑,但这实际上是语言设计的产物。请记住,您要使用的实际算法非常简单:确定要使用的指令,将它们加载到内存中的缓冲区中,然后跳转到该缓冲区的开头。

但是,如果您确实尝试执行此操作,请确保在返回C程序时正确使用了调用约定。我想如果我想生成代码,我会寻找一个图书馆来照顾我。 Nanojit最近成为新闻。你可以看一下。


对。您只需构建一个char *并执行它。但是,您需要注意一些细节。 char *必须在内存的可执行部分中,并且必须具有正确的对齐方式。

除了nanojit之外,您还可以签出LLVM,这是另一个库,能够将各种程序表示形式编译为功能指针。它的界面整洁,生成的代码趋向于高效。


Is it something as hacky as mapping
the mnemonic instructions to binary
codes, stuffing it into an char*
pointer and casting it as a function
and executing?

是的,那行得通。

要在Windows中执行此操作,必须将PAGE_EXECUTE_READWRITE设置为已分配的块:

1
2
3
4
5
void (*MyFunc)() = (void (*)()) VirtualAlloc(NULL, sizeofblock,  MEM_COMMIT, PAGE_EXECUTE_READWRITE);

//Now fill up the block with executable code and issue-

MyFunc();

关于生成DLL:为此需要额外的I / O,再加上链接,再加上生成DLL格式的复杂性,将使事情变得更加复杂,并且最重要的是,它们会破坏性能。此外,最后您仍然调用指向已加载代码的函数指针,因此...
而且,JIT编译一次只能发生一种方法,如果要这样做,则会生成许多小的DLL。

关于"可执行部分"的要求,在POSIX系统上调用mprotect()可以修复权限(Win32上有类似的API)。您需要对较大的内存段执行此操作,而不是对每个方法都执行一次,否则会太慢。

在纯x86上,您不会注意到该问题,在具有PAE或64位AMD64 / Intel 64位计算机的x86上,您会遇到段错误。


除了Rotor 2.0之外,您还可以查看OpenJDK中的HotSpot虚拟机。


据我所知,它必须编译内存中的所有内容,因为它必须运行一些试探法来优化代码(即:随着时间的推移而内联),但是您可以看看Shared Source Common Language Infrastructure 2.0转子版本。整个代码库与.NET相同,除了抖动和GC。


推荐阅读