JIT代码生成技术

时间:2008-09-05 08:07:00

标签: interpreter jit .net-assembly

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

假设您可以找出想要发出的本机机器操作码,那么您如何实际运行它?

将助记符指令映射到二进制代码,将其填充到char *指针并将其作为函数强制执行并执行是不是很麻烦?

或者你会生成一个临时共享库(.dll或.so或其他)并使用LoadLibrary等标准函数将其加载到内存中吗?

7 个答案:

答案 0 :(得分:8)

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

  

将助记符指令映射到二进制代码,将其填充到char *指针并将其作为函数强制执行并执行是不是很麻烦?

是。这是一种方法。生成的代码将转换为C中的pointer to function

答案 1 :(得分:6)

  

将助记符指令映射到二进制代码,将其填充到char *指针并将其作为函数强制执行并执行是不是很麻烦?

是的,如果你是用C或C ++(或类似的东西)做的,那就是你要做的。

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

但是,如果您确实尝试这样做,请确保在返回C程序时获得正确的调用约定。我想如果我想生成代码,我会寻找一个库来为我处理这个方面。 Nanojit最近出现在新闻中;你可以看一下。

答案 2 :(得分:4)

烨。您只需构建一个char *并执行它。但是,您需要注意几个细节。 char *必须位于内存的可执行部分,并且必须正确对齐。

除了nanojit,您还可以查看LLVM,这是另一个能够将各种程序表示编译为函数指针的库。它的界面很干净,生成的代码往往很有效。

答案 3 :(得分:1)

据我所知,它编译了内存中的所有内容,因为它必须运行一些启发式来优化代码(即:随时间推移内联),但是你可以查看Shared Source Common Language Infrastructure 2.0转子版本。除了Jitter和GC之外,整个代码库与.NET完全相同。

答案 4 :(得分:1)

与Rotor 2.0一样 - 您还可以查看OpenJDK中的HotSpot virtual machine

答案 5 :(得分:1)

关于生成DLL:额外需要的I / O,加上链接,再加上生成DLL格式的复杂性,会使事情变得更加复杂,最重要的是它们会破坏性能;另外,最后你仍然调用一个指向加载代码的函数指针,所以...... 此外,JIT编译可以一次发生一个方法,如果你想这样做,你会生成许多小DLL。

关于“可执行部分”的要求,在POSIX系统上调用mprotect()可以修复权限(Win32上有类似的API)。你需要为一个大内存段而不是每个方法执行一次,因为否则它会太慢。

在普通x86上你不会注意到这个问题,在带有PAE或64位AMD64 / Intel 64位机器的x86上你会遇到段错误。

答案 6 :(得分:1)

  

它是否像绘图一样hacky   二进制的助记符指令   代码,把它塞进一个char *   指针并将其作为函数转换   并执行?

是的,这有效。

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

void (*MyFunc)() = (void (*)()) VirtualAlloc(NULL, sizeofblock,  MEM_COMMIT, PAGE_EXECUTE_READWRITE);

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

MyFunc();