JIT代码生成技术

19

虚拟机如何实时生成本地机器码并执行?

假设你已经知道要发出哪些本地机器操作码,那么你该如何实际运行它呢?

是通过将助记符指令映射到二进制代码,将其填充到 char* 指针中并将其强制转换为函数并执行的hacky方法吗?

还是会生成临时共享库 (.dll 或 .so 或其他) 并使用标准函数 (例如 LoadLibrary) 将其加载到内存中?

7个回答

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

这是否像将助记符指令映射到二进制代码,将其放入char*指针中并将其强制转换为函数并执行一样神奇?

是的。这是其中一种方法。生成的代码将在C中转换为函数指针

6

如果你是在使用C或C++(或类似语言)进行操作,那么你需要做的就是将助记符指令映射到二进制代码中,将其塞入char*指针并将其强制转换为函数并执行。

是的,如果你使用C或C++(或类似语言)进行操作,这正是你需要做的。

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

然而,如果你真的想这样做,请确保在返回到C程序时正确获取调用约定。如果我想生成代码,我会寻找一个能为我处理这个方面的库。Nanojit最近被报道过,你可以查看一下。


4

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

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


1
这是一些黑客技巧吗?将助记符指令映射到二进制代码,将其填充到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();

1
关于生成DLL:所需的附加I/O,加上链接,再加上生成DLL格式的复杂性,会使其变得更加复杂,最重要的是它们会影响性能;此外,在最终阶段,您仍然需要调用指向已加载代码的函数指针,因此...
此外,JIT编译可以一次处理一个方法,如果您想这样做,您将生成许多小型DLL。
关于“可执行部分”的要求,在POSIX系统上调用mprotect()可以修复权限(Win32上有类似的API)。否则,您需要对一个大内存段而不是每个方法执行一次,否则速度会太慢。
在普通的x86上,您不会注意到问题,在带有PAE或64位AMD64 / Intel 64位机器的x86上,您会收到segfault。

1
据我所知,它会将所有内容编译到内存中,因为它必须运行一些启发式算法来优化代码(例如:随着时间的推移进行内联),但是您可以查看共享源代码公共语言基础结构2.0 rotor版本。整个代码库与.NET相同,除了Jitter和GC。

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接