原文地址:https://liveoverflow.com/just-in-time-compiler-in-javascriptcore-browser-0x03/

Introduction

在上一篇文章中,我们探讨了JavaScriptCore(来自WebKit的JavaScript引擎)是如何在内存中存储对象和数值的。在这篇文章中,我们将跟大家一起来探索JIT,即Just-In-Time编译器。

The Just-In-Time compiler

说到Just-In-Time编译器,这可是一个复杂的主题。但简单地说,JIT编译器的作用就是将JavaScript字节码(由JavaScript虚拟机执行)编译为本机机器代码(程序集)。虽然这个过程与C代码的编译过程非常相似,但是对于JavaScriptCore中的JIT来说,还是有许多独特之处的。

为了深入学习JIT,我曾经专门就此向Linus请教,他给出的建议是,多看官方的WebKit资源,例如JavaScriptCore CSI:A Crash Site Investigation Story,这篇文章在如何调试崩溃并诊断错误以查找根本原因方面给出了具体的操作方法——当我们在JavaScriptCore中挖掘漏洞的时候,这方面的内容是非常有用的。除此之外,这篇文章还介绍了如何创建WebKit的地址过滤程序,以用于捕获潜在的堆溢出或UAF漏洞。

$ cd webkitDIr
$ ./Tools/Scripts/set-webkit-configuration --asan
$ ./Tools/Scripts/build-webkit --debug

这些内容虽然很酷,但我们对JIT方面的内容更感兴趣,所以,让我们找一些对我们的研究有用的东西。在上面提到的文章中,我们可以找到有关JIT编译器相关介绍。

JSC是一种支持多级优化的执行引擎

实际上,它共有4级:

  • 第1级:LLInt解释器
  • 第2级:Baseline JIT编译器
  • 第3级:DFG JIT
  • 第4级:FTL JIT

第1级:LLInt解释器

这是常规的JavaScript解释器,实际上就是一个基本的JavaScript虚拟机。为了加深读者的立即,我们将通过源代码进行讲解。通过快速浏览LowLevelInterpreter.cpp源文件,我们会看到解释器的主循环语句,该循环用于遍历提供的JavaScript字节码,并执行各条指令。

//===================================================================================
// The llint C++ interpreter loop:
// LowLevelInteroreter.cpp

JSValue Cloop::execute(OpcodeID entry OpcodeID, void* executableAddress, 
VM* vm, ProtoCallFrame* protoCallFrame, bool isInitializationPass)
{
    // Loop Javascript bytecode and execute each instruction
    // [...] snip
}

第2级:Baseline JIT编译器

当一个函数被多次调用的时候,我们就可以称这个函数时一个“热门的(hot)”函数。这个术语用于表示其执行次数较多,而JavaScriptCore会将这种类型的函数JIT化。如果我们查看JIT.cpp源文件,我们可以进一步获得更加详细的信息。

void JIT::privateCompileMainPass()
{
    ...
    // When the LLInt determines it wants to do OSR entry into the baseline JIT in a loop,
    // it will pass in the bytecode offset it was executing at when it kicked off our 
    // compilation. We only need to compile code for anything reachable from that bytecode 
    // offset.
    ...
}

当前栈替换(On Stack Replacement,OSR)是一种用于在同一函数的不同实现之间进行切换的技术。例如,OSR可以在执行期间从动态解释代码(未优化代码)切换为对应的JIT编译代码。关于OSR的更多介绍,请参阅这里

然而,在这个阶段,机器代码仍然与原始字节码非常兼容的(我不知道使用这个术语进行描述是否恰当)的,并且还没有进行真正的优化处理。

第3级:DFG JIT

另一篇介绍WebKit FTL JIT的文章,曾经指出:

任何函数的第一次执行总是从解释器级开始的不过一旦函数中的某个语句执行次数超过了100次或者某个函数被调用次数超过6次以先到者为准),执行权就会转移给Baseline JIT编译的代码这种做法虽然能够降低解释器的开销但并没有进行真正意义上的编译器优化一旦任何语句在Baseline代码中执行次数超过1000次或者Baseline函数被调用次数超过66次我们会再次将执行权转移给DFG JIT编译的代码
点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖