javascript|V8 编译浅谈( 四 )


(0x7fb8c080e630) (mode = TEMPORARY assigned = true) \".result\"[generating bytecode for function: add
--- AST ---FUNC at 12. KIND 0. LITERAL ID 1. SUSPEND COUNT 0. NAME \"add\". PARAMS. . VAR (0x7fb8c080e4d8) (mode = VAR assigned = false) \"x\". . VAR (0x7fb8c080e580) (mode = VAR assigned = false) \"y\". DECLS. . VARIABLE (0x7fb8c080e4d8) (mode = VAR assigned = false) \"x\". . VARIABLE (0x7fb8c080e580) (mode = VAR assigned = false) \"y\". RETURN at 25. . ADD at 34. . . VAR PROXY parameter[0
(0x7fb8c080e4d8) (mode = VAR assigned = false) \"x\". . . VAR PROXY parameter[1
(0x7fb8c080e580) (mode = VAR assigned = false) \"y\" 我们以图形化的方式来描述生成的 AST 树:

VAR PROXY 节点在真正的分析阶段会连接到对应地址的 VAR 节点 。
3 生成字节码
AST 会经过 Ignition 解释器的 BytecodeGenerator 函数生成字节码(中间表示) , 我们可以通过 --print-bytecode 参数来打印字节码信息:
v8-debug --print-bytecode ./index.js[generated bytecode for function: (0x3ab2082933f5SharedFunctionInfo)
Bytecode length: 43Parameter count 1Register count 6Frame size 48OSR nesting level: 0Bytecode Age: 0 0x3ab2082934be @ 0 : 13 00 LdaConstant [0
0x3ab2082934c0 @ 2 : c3 Star1 0x3ab2082934c1 @ 3 : 19 fe f8 Movclosure r2 0x3ab2082934c4 @ 6 : 65 52 01 f9 02 CallRuntime [DeclareGlobals
r1-r2 0x3ab2082934c9 @ 11 : 21 01 00 LdaGlobal [1
[0
0x3ab2082934cc @ 14 : c2 Star2 0x3ab2082934cd @ 15 : 2d f8 02 02 LdaNamedProperty r2 [2
[2
0x3ab2082934d1 @ 19 : c3 Star1 0x3ab2082934d2 @ 20 : 21 03 04 LdaGlobal [3
[4
0x3ab2082934d5 @ 23 : c1 Star3 0x3ab2082934d6 @ 24 : 0d 01 LdaSmi [1
0x3ab2082934d8 @ 26 : c0 Star4 0x3ab2082934d9 @ 27 : 0d 02 LdaSmi [2
0x3ab2082934db @ 29 : bf Star5 0x3ab2082934dc @ 30 : 63 f7 f6 f5 06 CallUndefinedReceiver2 r3 r4 r5 [6
0x3ab2082934e1 @ 35 : c1 Star3 0x3ab2082934e2 @ 36 : 5e f9 f8 f7 08 CallProperty1 r1 r2 r3 [8
0x3ab2082934e7 @ 41 : c4 Star0 0x3ab2082934e8 @ 42 : a9 Return Constant pool (size = 4)0x3ab208293485: [FixedArray
in OldSpace - map: 0x3ab208002205Map- length: 4 0: 0x3ab20829343dFixedArray[2
1: 0x3ab208202741String[7
: #console2: 0x3ab20820278dString[3
: #log3: 0x3ab208003f09String[3
: #addHandler Table (size = 0)Source Position Table (size = 0)[generated bytecode for function: add (0x3ab20829344dSharedFunctionInfo add)
Bytecode length: 6// 接受 3 个参数 ,1 个隐式的 this , 以及显式的 x 和 yParameter count 3Register count 0// 不需要局部变量 , 因此帧大小为 0 Frame size 0OSR nesting level: 0Bytecode Age: 0 0x3ab2082935f6 @ 0 : 0b 04 Ldar a1 0x3ab2082935f8 @ 2 : 39 03 00 Add a0 [0
0x3ab2082935fb @ 5 : a9 Return Constant pool (size = 0)Handler Table (size = 0)Source Position Table (size = 0) add 函数主要包含以下 3 个字节码序列:
// Load Accumulator Register// 加载寄存器 a1 的值到累加器中Ldar a1// 读取寄存器 a0 的值并累加到累加器中 , 相加之后的结果会继续放在累加器中// [0
指向 Feedback Vector Slot , Ignition 会收集值的分析信息 , 为后续的 TurboFan 优化做准备Add a0 [0
// 转交控制权给调用者 , 并返回累加器中的值Return 这里 Ignition 的解释执行这些字节码采用的是一地址指令结构的寄存器架构 。
关于更多字节码的信息可查看 Understanding V8’s Bytecode 。
4 优化和去优化
JavaScript 是弱类型语言 , 不会像强类型语言那样需要限定函数调用的形参数据类型 , 而是可以非常灵活的传入各种类型的参数进行处理 , 如下所示:
function add(x y) { // + 操作符是 JavaScript 中非常复杂的一个操作 return x + yadd(1 2);add('1' 2);add( 2);add(undefined 2);add([
2);add({ 2);add([
{); 为了可以进行 + 操作符运算 , 在底层执行的时候往往需要调用很多 API , 比如 ToPrimitive(判断是否是对象)、ToString、ToNumber 等 , 将传入的参数进行符合 + 操作符的数据转换处理 。