【问题标题】:How do JIT compilers stay ahead of executing machine code?JIT 编译器如何在执行机器代码之前保持领先?
【发布时间】:2018-06-18 02:43:50
【问题描述】:

如果我理解正确,JIT 编译器会即时将代码(通常是字节码)编译为本机机器码,并将其插入已知内存中的适当位置。

一旦启动该进程,JIT 编译器如何保持领先于正在执行的机器代码?如何确保执行代码不会遇到用 GOTO 或等效项指向的空白内存,因为 JIT 还没有弄清楚接下来要放什么?

例如,给定一些(假)字节码:

03 01 move variable 1 onto the stack
b3 02 do something with the contents

在生成第一行本机代码并放入要运行的下一行之后,我假设 JIT 会给本机代码一个“GOTO”到一个空的内存集,以便在其中运行下一批指示。但是,如果机器码在 JIT 编译器有时间将第 2 行的机器码放入该槽之前到达那里怎么办?

【问题讨论】:

  • 此主题的wikipedia 是否提供详细信息?
  • 正在编译的代码在编译完成之前不会运行。一旦一个方法(或另一个编译单元)被完全编译,执行入口就会自动切换到一个新的编译版本。

标签: jvm executable jit machine-code


【解决方案1】:

通过以下两条规则确保正确性:

绝不允许执行未完成的代码

JIT 编译器将首先完成其工作的任何代码区域的编译,这可能是一个基本块、一个函数或对代码的任意跟踪。只有在它完成后,它才会允许处理器执行该代码。所以执行永远不会遇到未完成的翻译。

不要生成未定义的跳转

每当 JIT 编译器遇到一个离开正在编译的代码区域的跳转时,它都会生成一个跳转回解释器代码,该代码可能通过编译其他代码区域来确定继续执行的位置,但是 从不未定义的位置。在编译区域的末尾也是如此。

一些 JIT 还编译为遵循机器调用约定的函数,因此可以只使用普通返回(LLVM JIT 就是一个例子)。在这种情况下,“JITed”代码只是通过函数指针调用,代码只是返回给调用者,即解释器。

其他 JIT 编译器为生成的代码生成自定义序言和结尾,以确保处理器在执行 jit 代码后处于定义的状态,并且继续执行所需的所有信息都可用。

作为一种优化,JIT 可能会注意到跳转到已经 JIT 编译或静态预编译的代码(例如库函数),并在那里发出直接跳转,或者他们可以创建跳转指令稍后可以对其进行修补以转到新编译的代码段(QEMU 会这样做)。

【讨论】:

  • 谢谢!这就是我一直在寻找的。​​span>
【解决方案2】:

这因具体实现而异。可能的策略是在解释器中运行代码 - 或者在分层 JIT 的情况下运行之前的编译层 - 同时编译或暂停执行直到编译完成。

【讨论】:

    猜你喜欢
    • 2015-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    • 2021-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多