《深刻V8引擎-第01課》

 

深刻V8引擎-第01課:上手開始看 V8 Ignition 解釋器的字節碼(Bytecodes)

 
 

 

 

 

 

 

 

 

 

 

 

 

 

 V:傳函數名調用java

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第一列是地址node

相對偏移git

a7 11 對應的是字節碼(字節碼對應的二進制數字實例,轉化成16進制的樣子)github

助記符:LdaFalse —— LD 是 load,a 是accumulator,False/Zero 等都專門分配了一個字節碼,進行過特殊化的處理性能優化

 

 

 

 

v8引擎是如何知道js數據類型的? - 知乎

www.zhihu.com › question
 
SMI 的意思就是Small Integer 的縮寫,SMI 對應這Int31(32位系統是Int30)。 V8 使用了一個技巧「tagged pointers」來區分這個變量是一個SIM,仍是HeapObject 。 Smi ...
 

 

 

原文: Understanding V8’s Bytecode
做者: Franziska Hinkelmann
譯者: justjavac

V8 是 Google 開發的開源 JavaScript 引擎。 Chrome、Node.js和許多其餘應用程序都在使用 V8。本文介紹了 V8 的字節碼格式—— 若是你瞭解關於字節碼的基本概念,讀起來會更加輕鬆。ssh

當 V8 編譯 JavaScript 代碼時,解析器(parser)將生成一個抽象語法樹。語法樹是 JavaScript 代碼的句法結構的樹形表示形式。解釋器 Ignition 根據語法樹生成字節碼。TurboFan 是 V8 的優化編譯器,TurboFan 將字節碼生成優化的機器代碼。ide

若是想知道爲何會有兩種執行模式,能夠從 JSConfEU 查看我(原做者)的視頻(YouTube需科學地上網):函數

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

 

字節碼是機器代碼的抽象。若是字節碼採用和物理 CPU 相同的計算模型進行設計,則將字節碼編譯爲機器代碼更容易。這就是爲何解釋器(interpreter)經常是寄存器或堆棧。 Ignition 是具備累加器的寄存器。性能

您能夠將 V8 的字節碼看做是小型的構建塊(bytecodes as small building blocks),這些構建塊組合在一塊兒構成任何 JavaScript 功能。V8 有數以百計的字節碼。好比 Add 或 TypeOf 這樣的操做符,或者像 LdaNamedProperty 這樣的屬性加載符,還有不少相似的字節碼。 V8還有一些很是特殊的字節碼,如 CreateObjectLiteral 或 SuspendGenerator。頭文件 bytecodes.h 定義了 V8 字節碼的完整列表

每一個字節碼指定其輸入和輸出做爲寄存器操做數。Ignition 使用寄存器 r0,r1,r2,... 和累加器寄存器(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 <Map(HOLEY_ELEMENTS)>
 - length: 1
           0: 0x2ddf8db91611 <String[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 <String[1]: x>

能夠看到,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 版本中,細節可能會有所不一樣。

------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 是整數加

仍是字符串拼接加

是不知道的

 

字符串 + 數字:數字轉字符串

數字 + 字符串:可能就報錯了

 

 

 

簡單的:LD ST 

複雜的:callruntime、 jit 、tracing 相關的操做

 

d8 -help

dump 能夠 dump出/trace:bytecode、ast、cfnotes?、各類IR的結果、包括到最後二進制的結果、整個內存GC的過程 

靜態:dump,觀察堆棧是否出錯

動態:gdb 

相關文章
相關標籤/搜索