What is It?

Ignition là một trình thông dịch dựa trên thanh ghi của V8. Các thanh ghi mà nó sử dụng không thật sự là thanh ghi của máy tính mà là các slot cụ thể ở trong register file mà được cấp phát ở trên stack.

Bytecode Handlers

Ignition chứa một tập các bytecode handler dùng để thực thi bytecode và sẽ được biên dịch bởi V8 - Turbo Fan khi trình duyệt được biên dịch. Từng handler sẽ xử lý từng bytecode cụ thể và sẽ dispatch đến handler tiếp theo.

Khi V8 tạo ra một isolate mới, nó sẽ nạp tất cả các handler từ một snapshot file được tạo ra trong quá trình build.

Ngoài ra, isolate còn có một global dispatch table chứa các con trỏ đến các Code object (là các object chứa code) của các bytecode handler và được đánh index bằng giá trị của bytecode.

Generating Bytecode

Ignition sẽ lấy abtract syntax tree (AST) từ parser và duyệt qua từng node để tạo ra các bytecode tương ứng. V8 sử dụng hàm GenerateBytecode thuộc lớp BytecodeGenerator để làm điều này.

Mỗi hàm của JavaScript đều được V8 biểu diễn bằng một JSFunction object. Thuộc tính shared có kiểu là SharedFunctionInfo (SFI) trong JSFunction sẽ lưu bytecode và được chia sẻ bởi những instance khác nhau của hàm.

Trong quá trình tạo ra bytecode, BytecodeGenerator cũng sẽ cấp phát các thanh ghi cần dùng trong quá trình thực thi của Ignition vào một register file cho các biến cục bộ, các con trỏ của context và các giá trị dùng tạm.

Bytecode

Xét hàm sau:

function incX(obj) { 
	return 1 + obj.x; 
}

Bytecode của nó sẽ là:

[generated bytecode for function: incX (0x026c0025ab65 <SharedFunctionInfo incX>)]
Bytecode length: 11
Parameter count 2
Register count 1
Frame size 8
Bytecode age: 0
         0000026C0025ACC6 @    0 : 0d 01             LdaSmi [1]
         0000026C0025ACC8 @    2 : c5                Star0
         0000026C0025ACC9 @    3 : 2d 03 00 01       GetNamedProperty a0, [0], [1]
         0000026C0025ACCD @    7 : 39 fa 00          Add r0, [0]
         0000026C0025ACD0 @   10 : aa                Return
Constant pool (size = 1)
0000026C0025AC99: [FixedArray] in OldSpace
 - map: 0x026c00002231 <Map(FIXED_ARRAY_TYPE)>
 - length: 1
           0: 0x026c000041ed <String[1]: #x>
Handler Table (size = 0)
Source Position Table (size = 0)
14

Các toán hạng chẳng hạn như a0r0 là các thanh ghi. Với:

  • a0 tương ứng với đối số đầu tiên. Các đối số tiếp theo sẽ là a1, a2, a3, etc.
  • r0 tương ứng với biến cục bộ.

Ngoài những thanh ghi thông thường, Ignition còn có thanh ghi accumulator (hay acc), đóng vai trò như là thanh ghi lưu trữ giá trị trung gian trong quá trình tính toán và được ngầm định ở đa số các bytecode. Ví dụ, bytecode Return sẽ trả về giá trị của thanh ghi acc.

Info

Việc sử dụng acc ngầm định giúp giảm kích thước bytecode.

Giải thích đoạn bytecode trên:

  • LdaSmi [1] lưu số 1 vào acc:
    • Ld là dạng rút gọn của load
    • a là để chỉ accumulator
    • Smi là viết tắt của small integer
  • Star0 lưu giá trị của acc (1) vào r0:
    • St là dạng rút gọn của store
    • a là để chỉ accumulator
    • r0 là để chỉ thanh ghi r0
  • GetNamedProperty a0, [0], [1] lưu thuộc tính ở index 0 ([0]) của a0 vào acc. Giá trị [1] là feedback vector mà sẽ được dùng bởi Turbo Fan
  • Add r0, [0] cộng r0 với acc và lưu giá trị vào acc ([0] là feedback vector)
  • Return trả về giá trị của acc.

Seealso

Có thể xem danh sách các bytecode ở tập tin bytecodes.h.

Stack Frame Generation and Execution

Sau khi bytecode được tạo ra, thuộc tính code_entry_point sẽ trỏ đến một built-in stub có tên là InterpreterEntryTrampoline.

Lưu ý, InterpreterEntryTrampoline chỉ được gán cho code_entry_point khi một hàm của JavaScript được gọi (nếu hàm không được gọi thì stub của nó sẽ là CompileLazy và bytecode sẽ không được sinh ra). InterpreterEntryTrampoline chịu trách nhiệm thiết lập stack frame cho Ignition và dispatch bytecode đầu tiên trong hàm đến bytecode handler của Ignition.

Trong quá trình thiết lập stack frame, InterpreterEntryTrampoline sẽ cấp phát không gian bộ nhớ ở trên stack cho register file và ghi giá trị undefined cho tất cả các thanh ghi để garbage collector không xem các thanh ghi này là bất hợp lệ khi nó duyệt qua stack.

Bố cục của stack frame sau quá trình thiết lập của InterpreterEntryTrampoline:

Ignition stack frame sẽ bao gồm:

  • Context: context hiện tại của isolate.
  • $PC: pointer counter của caller.
  • JSFunction: con trỏ trỏ đến JSFunction của hàm.

Cấu trúc của JSFunction:

Có thể thấy, JSFunction bao gồm:

  • Context: context của hàm, chứa các biến cục bộ và đối tượng this.
  • SharedFunctionInfo: chứa bytecode.
  • Feedback Vector: chứa các inline cache của hàm.

Thanh ghi accumulator sẽ được lưu trong đối tượng được trỏ đến bởi Frame Pointer:

Con trỏ BytecodeArray sẽ trỏ đến đối tượng có chứa chuỗi các bytecode của một hàm cụ thể ở trên stack frame. Bên trong BytecodeArray còn có một con trỏ có tên là Constant Pool Pointer và sẽ trỏ đến các đối tượng ở trên heap mà được xem như là hằng số và có thể được tái sử dụng giữa các bytecode:

Execution

Để thực thi một bytecode, Ignition sẽ cần phải tìm handler tương ứng của bytecode để dispatch.

Xét một BytecodeArray sau:

d8> var num = 42;
[generated bytecode for function:  (0x03650025a599 <SharedFunctionInfo>)]
Bytecode length: 18
Parameter count 1
Register count 3
Frame size 24
Bytecode age: 0
	  000003650025A61E @    0 : 13 00             LdaConstant [0]
	  000003650025A620 @    2 : c4                Star1
	  000003650025A621 @    3 : 19 fe f8          Mov <closure>, r2
	  000003650025A624 @    6 : 66 5f 01 f9 02    CallRuntime [DeclareGlobals], r1-r2
	  000003650025A629 @   11 : 0d 2a             LdaSmi [42]
	  000003650025A62B @   13 : 23 01 00          StaGlobal [1], [0]
	  000003650025A62E @   16 : 0e                LdaUndefined
	  000003650025A62F @   17 : aa                Return

Dòng bytecode đầu tiên có giá trị là 13 00. Trong đó, 0x13 là giá trị của bytecode LdaConstant ở dạng thập lục phân. Dựa vào giá trị này, Ignition sẽ tra ở trong global dispatch table để tìm handler tương ứng. Sau đó, Ignition sẽ dùng hàm SetBytecodeHandler với đối số là bytecode, các toán hạng và handler để thiết lập handler cho bytecode.

Sau đó, Ignition sẽ dispatch bytecode đến cho bytecode handler để thực thi hàm.

list
from outgoing([[Ignition]])
sort file.ctime asc

Resources