热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

字节码开发笔记:深入解析与应用技巧

篇首语:本文由编程笔记#小编为大家整理,主要介绍了字节码相关的知识,希望对你有一定的参考价值。 了解 V8 的字节码「译」Javascript 阅读约 9 分钟原文:Understandin

篇首语:本文由编程笔记#小编为大家整理,主要介绍了字节码相关的知识,希望对你有一定的参考价值。



了解 V8 的字节码「译」

Javascript

 阅读约 9 分钟


原文:Understanding V8’s Bytecode
作者:Franziska Hinkelmann
译者:justjavac


V8 是 Google 开发的开源 Javascript 引擎。 Chrome、Node.js和许多其他应用程序都在使用 V8。本文介绍了 V8 的字节码格式—— 如果你了解关于字节码的基本概念,读起来会更加轻松。

技术图片

当 V8 编译 Javascript 代码时,解析器(parser)将生成一个抽象语法树。语法树是 Javascript 代码的句法结构的树形表示形式。解释器 Ignition 根据语法树生成字节码。TurboFan 是 V8 的优化编译器,TurboFan 将字节码生成优化的机器代码。

技术图片

如果想知道为什么会有两种执行模式,可以从 JSConfEU 查看我(原作者)的视频(YouTube需fq上网):

Franziska Hinkelmann: Javascript engines - how do they even? | JSConf EU 2017

技术图片

字节码是机器代码的抽象。如果字节码采用和物理 CPU 相同的计算模型进行设计,则将字节码编译为机器代码更容易。这就是为什么解释器(interpreter)常常是寄存器或堆栈。 Ignition 是具有累加器的寄存器。

技术图片

您可以将 V8 的字节码看作是小型的构建块(bytecodes as small building blocks),这些构建块组合在一起构成任何 Javascript 功能。V8 有数以百计的字节码。比如 Add 或 TypeOf 这样的操作符,或者像 LdaNamedProperty 这样的属性加载符,还有很多类似的字节码。 V8还有一些非常特殊的字节码,如 CreateObjectLiteral 或 SuspendGenerator。头文件 bytecodes.h 定义了 V8 字节码的完整列表。

每个字节码指定其输入和输出作为寄存器操作数。Ignition 使用寄存器 r0r1r2,... 和累加器寄存器(accumulator register)。几乎所有的字节码都使用累加器寄存器。它像一个常规寄存器,除了字节码没有指定。 例如,Add r1 将寄存器 r1 中的值和累加器中的值进行加法运算。这使得字节码更短,节省内存。

许多字节码以 Lda 或 Sta 开头。Lda 和 Stastands 中的 a 为累加器(accumulator)。例如,LdaSmi [42] 将小整数(Smi)42 加载到累加器寄存器中。Star r0 将当前在累加器中的值存储在寄存器 r0 中。

以现在掌握的基础知识,花点时间来看一个具有实际功能的字节码。

function incrementX(obj) {
return 1 + obj.x;
}
incrementX({x: 42});
// V8 的编译器是惰性的,
// 如果一个函数没有运行,V8 将不会解释它

如果要查看 V8 的 Javascript 字节码,可以使用在命令行参数中添加 --print-bytecode 运行 D8 或Node.js(8.3 或更高版本)来打印。对于 Chrome,请从命令行启动 Chrome,使用 --js-flags="--print-bytecode",请参考 Run Chromium with flags。


$ node --print-bytecode incrementX.js
...
[generating bytecode for function: incrementX]
Parameter count 2
Frame size 8
12 E> 0x2ddf8802cf6e @ StackCheck
19 S> 0x2ddf8802cf6f @ LdaSmi [1]
0x2ddf8802cf71 @ Star r0
34 E> 0x2ddf8802cf73 @ LdaNamedProperty a0, [0], [4]
28 E> 0x2ddf8802cf77 @ Add r0, [6]
36 S> 0x2ddf8802cf7a @ Return
Constant pool (size = 1)
0x2ddf8802cf21: [FixedArray] in OldSpace
- map = 0x2ddfb2d02309
- length: 1
0: 0x2ddf8db91611 1]: x>
Handler Table (size = 16)

我们忽略大部分输出,专注于实际的字节码。

这是每个字节码的意思,每一行:


LdaSmi [1]

LdaSmi [1] 将常量 1 加载到累加器中。

技术图片


Star r0

接下来,Star r0 将当前在累加器中的值 1 存储在寄存器 r0 中。

技术图片


LdaNamedProperty a0, [0], [4]

LdaNamedProperty 将 a0 的命名属性加载到累加器中。ai 指向 incrementX() 的第 i 个参数。在这个例子中,我们在 a0 上查找一个命名属性,这是 incrementX() 的第一个参数。该属性名由常量 0 确定。LdaNamedProperty 使用 0 在单独的表中查找名称:

- length: 1
0: 0x2ddf8db91611

可以看到,0 映射到了 x。因此这行字节码的意思是加载 obj.x

那么值为 4 的操作数是干什么的呢? 它是函数 incrementX() 的反馈向量的索引。反馈向量包含用于性能优化的 runtime 信息。

现在寄存器看起来是这样的:

技术图片


Add r0, [6]

最后一条指令将 r0 加到累加器,结果是 43。 6 是反馈向量的另一个索引。

技术图片


Return

Return 返回累加器中的值。返回语句是函数 incrementX() 的结束。此时 incrementX() 的调用者可以在累加器中获得值 43,并可以进一步处理此值。

乍一看,V8 的字节码看起来非常奇怪,特别是当我们打印出所有的额外信息。但是一旦你知道 Ignition 是一个带有累加器寄存器的寄存器,你就可以分析出大多数字节码都干了什么。

Learned something? Clap your ? to say “thanks!” and help others find this article.


注意:此处描述的字节码来自 V8 版本 6.2,Chrome 62 以及 Node 9(尚未发布)版本。我们始终致力于 V8 以提高性能和减少内存消耗。在其他 V8 版本中,细节可能会有所不同。




欢迎关注我的公众号,关注前端文章:

技术图片


推荐阅读
author-avatar
藏A组合别_577
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有