我最接近汇编的是构建我自己的 Java 类库,它加载类文件并允许您创建、编译和反编译类。在努力完成这个项目时,我想知道 Java 虚拟机实际上是如何在 JIT 优化期间在运行时生成 native 机器代码的。
这让我开始思考:如何在没有 JIT 编译器库或“手动”的情况下生成机器代码并在运行时使用汇编执行它,作为奖励?
最佳答案
您的问题发生了重大变化(2017 年 7 月)。初始变体指的是 EX (execute) instruction IBM 大型机。
how could one generate machine code and execute it at runtime with assembly...?
在实践中,你会使用一些 JIT compilation图书馆,其中有很多。或者你会使用一些 dynamic loader .在最底层,他们都写了一些字节序列来表示有效的machine code。 — 许多机器指令的序列 — 在内存段中(您的 virtual address space )必须制作 executable (阅读 NX bit ),然后您的一些代码将间接跳转到该地址或更经常地间接调用它——即通过 function pointer 调用.大多数JVM实现使用 JIT 编译技术。
...and as a bonus, without a JIT compiler library, or "manually"?
假设您有一些有效的机器代码用于您的程序当前正在执行的处理器架构,例如,您可以获得一个内存段(例如 Linux 上的 mmap(2)),然后使其可执行(例如 mprotect(2)) ).大多数其他operating systems提供类似system calls .
如果你使用像asmjit 这样的JIT 编译库或 libjit或 libgccjit或 LLVM或许多其他方法,您首先在内存中构建要生成的代码的表示形式(类似于某些 abstract syntax tree ),然后要求 JIT 库为其发出机器代码。您甚至可以编写自己的 JIT 编译代码,但这是一项很多的工作(您需要了解 instruction set 的所有细节,例如 x86 用于 PC)。顺便说一下,生成快速运行的机器代码真的很困难,因为你需要 optimize喜欢compilers做(并关心像 instruction scheduling 、 register allocation 等细节......另见 this ),这就是为什么使用现有的 JIT 编译库(如 libgccjit 或 LLVM )更可取(相反,更简单的 JIT 库,如 asmjit 或 libjit 或 GNU lightning 不会优化太多并生成糟糕的机器代码)。
如果您使用 dynamic loader (例如 dlopen(3) 在 POSIX 上)你会使用一些外部编译器来生成一个共享库(即 plugin )然后你要求动态链接器在你的进程中加载它(并处理适当的 relocations )并得到按名称(使用 dlsym(3) )其中的一些函数地址。
一些语言实现(特别是 Common Lisp 的 SBCL)能够在每个 REPL 处即时发出一些好的机器代码。相互作用。本质上,他们的运行时启动了一个完整的编译器(包含一个 JIT 编译部分)。
我经常用的一个技巧use在 Linux 上是在运行时在某个临时文件中发出一些 C(或 C++)代码(即将某些领域特定语言编译为 C 或 C++),fork compilation将其作为插件,并动态加载它。对于当前的(笔记本电脑、台式机、服务器)计算机,它的速度足以与交互式循环保持兼容。
另请阅读 eval (特别是著名的 SICP 书),metaprogramming , multistage programming , self-modifying code , continuations , 编译器 (Dragon Book), Scott 的 Programming Language Pragmatics , 和 J.Pitrat's blog .
关于language-agnostic - 我如何在运行时生成和执行机器代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43255053/