认识v8编译的字节码(bytecode)

By
写代码

js代码在v8的编译过程

代码需要编译为机器代码,才能最终在目标机器上执行。v8为了做到这一点,设计了中间层-字节码(bytecode)。

为了方便转译为机器代码,字节码采用了和物理CPU类似的带寄存器-累加器的计算模型。它的理解难度介于高级语言和汇编语言中间。

这个代码到bytecode的转译过程发生在Ignition里;而从bytecode到机器代码,则在TurboFan里进行。

v8-compile-code
v8引擎编译代码的过程

了解bytecode

bytecode更偏向底层一些,比console.log更直观,一步一步检查很方便。不过语法更像汇编,所以理解上需要一些时间。

举个例子

其中的字符串'string'和对象{a: 0}会保存在堆,保留一个内存地址。

字节码输出如下

我们只取中间执行部分出来看看,关于语法的定义,直接去 v8的js-graph.cc 查找

有了上面的例子,我们立刻写一个经典的变量提升的问题

对应的字节码

可以看出,除了高亮的部分,余下的部分是一样的。

可以看出,编译器预留了r0寄存器,来保存'string'地址,调用console.log的当下,r0还没有赋值。所以我们看到的结果就是输出了'undefined'

那么如果用let声明会发生什么?自己试试吧,会遇到坑(hole)。

如何输出字节码

发行版的node环境,执行node --print-bytecode /path/to/target.js就会输出字节码。

但是release版本constant pool默认不显示,需要手动编译debug版本的v8才能看到。编译的方法在这里:Building V8 with GNV8 bytecode 系列文介紹

附:堆栈、寄存器、累加器

堆(heap)是不连续的内存区域,主要存放对象等。

栈(stack)是一块连续的内存区域,按照一定顺序存放,栈中主要存放基本类型变量的值以及指向堆中的数组或者对象的地址。

寄存器(register)有很多个,用于保存基本变量和引用变量的地址。

累加寄存器(accumulator register)用于保存中间计算的结果。

参考页面:

0

Comments: 0

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*

:razz: