14

I've written a Brainfuck implementation (C++) that works like this:

  1. Read input brainfuck file
  2. Do trivial optimizations
  3. Convert brainfuck to machine code for the VM
  4. Execute this machine code in the VM

This is pretty fast, but the bottleneck is now at the VM. It's written in C++ and reads a token, executes an action (which aren't many at all, if you know Brainfuck) and so on.

What I want to do is strip out the VM and generate native machine code on the fly (so basicly, a JIT compiler). This can easily be a 20x speedup.

This would mean step 3 gets replaced by a JIT compiler and step 4 with the executing of the generated machine code.

I don't know really where to start, so I have a few questions:

  1. How does this work, how does the generated machine code get executed?
  2. Are there any C++ libraries for generating native machine code?
4

4 回答 4

17
  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 的完整 LLVM JIT 编译器http://www.remcobloemen.nl/2010/02/brainfuck-using-llvm/

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

于 2011-05-13T01:49:32.093 回答
6

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

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

于 2011-05-13T01:52:25.883 回答
4

这可能会迟到,但为了帮助其他任何人,我发布了这个答案。

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

于 2018-08-14T10:34:42.703 回答
3

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

于 2011-05-13T01:49:15.047 回答