制作 JIT 编译器

Making a JIT compiler

本文关键字:编译器 JIT 制作      更新时间:2023-10-16

我写了一个Brainfuck实现(C++),工作原理如下:

  1. 读取输入脑文件
  2. 进行琐碎的优化
  3. 将大脑转换为虚拟机的机器代码
  4. 在 VM 中执行此机器代码

这非常快,但现在瓶颈在 VM 上。它是用C++编写的,读取一个令牌,执行一个动作(如果你知道Brainfuck的话,这根本不多)等等。

我想做的是剥离 VM 并动态生成本机机器代码(所以基本上是一个 JIT 编译器)。这很容易实现 20 倍的加速。

这意味着步骤 3 被 JIT 编译器取代,步骤 4 被执行生成的机器代码所取代。

我不知道从哪里开始,所以我有几个问题:

  1. 这是如何工作的,生成的机器代码是如何执行的?
  2. 是否有用于生成本机机器代码的C++库?
  1. 生成的机器代码只是像往常一样jmp -ed 或 call -ed。有时它还需要在包含生成代码的内存上禁用无执行标志(NX 位)。在 Linux 中,这是通过 mprotect(addr, size, PROT_READ | PROT_WRITE | PROT_EXEC.) 在 Windows 中,NX 称为 DEP。

  2. 有一些...例如 http://www.gnu.org/software/lightning/- GNU Lightning(通用)和 https://developer.mozilla.org/En/Nanojit - Nanojit,用于Firefox JavaScript JIT引擎。更强大和现代的JIT是LLVM,你只需要将BF代码转换为LLVM IR,然后LLVM就可以对许多平台进行优化和代码生成,或者在具有JIT功能的解释器(虚拟机)上运行LLVM IR。有一篇关于BF和LLVM的文章,它为BF http://www.remcobloemen.nl/2010/02/brainfuck-using-llvm/提供了完整的LLVM JIT编译器

另一个BF + LLVM编译器在这里,在LLVM的svn中:https://llvm.org/svn/llvm-project/llvm/trunk/examples/BrainF/BrainF.cpp

LLVM是一个完整的C++库(或一组库),用于从中间形式生成本机代码,包括文档和示例,并已用于生成JITter。

(它还有一个使用该框架的C/C++编译器 - 但是框架本身可用于其他语言)。

这可能已经晚了,但为了帮助任何其他人,我发布了这个答案。

JIT 编译器具有 AOT 编译器具有的所有步骤。主要区别在于 AOT 编译器将机器相关代码输出到可执行文件(如 exe 等),而 JIT 编译器在运行时将计算机相关代码加载到内存中(因此性能开销,因为每次都需要重新编译和加载)。

JIT 编译器如何在运行时将机器代码加载到内存中?

我不会教你机器代码,因为我假设你已经知道它,

例如汇编代码

mov    rax,0x1

被翻译成

48 c7 c0 01 00 00 00

您动态生成翻译后的代码并将其保存到这样的向量中(这是一个 C 向量)

vector machineCode{
   0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 
}

然后你把这个向量复制到内存中,为此你需要知道这个代码所需的内存大小,你可以通过machinecode.size()获得,并记住页面大小。

要将此向量复制到内存中,您需要在 C 中调用 mmap 函数。将指针设置为代码的开头并调用它。你很好去。

抱歉,如果有什么不清楚的地方,为了简单起见,您可以随时查看这篇文章https://solarianprogrammer.com/2018/01/10/writing-minimal-x86-64-jit-compiler-cpp/https://github.com/spencertipping/jit-tutorial

GNU Lightning 是一组宏,可以为几种不同的架构生成本机代码。 您将需要对汇编代码有深入的了解,因为您的步骤 3 将涉及使用 Lightning 宏将机器代码直接发送到稍后将执行的缓冲区中。