我的機器是MacOS Mx系列。
一、v8源碼下載構建
1.1 下載并更新depot_tools
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=/path/to/depot_tools:$PATH
失敗的話可能是網絡問題,可以試一下是否能ping通,連接這個google的網站需要配置proxy才可以,不然會一直失敗:
# 給git配置proxy
git config --global http.proxy 127.0.0.1:代理端口
# 給終端配置proxy
export http_proxy=http://localhost:代理端口
export https_proxy=http://localhost:代理端口
配置完proxy之后我們再下載的話應該就沒有問題了。然后我們可以更新depot_tools工具:
gclient
depot_tools 是一個由 Google 開發的工具集,主要用于管理 Chromium 和 Chromium OS 項目的代碼倉庫、代碼審查和構建過程。它包含了許多實用的工具,例如:
- gclient: 用于管理 Chromium 項目的代碼庫,包括下載、同步和更新代碼。
- git cl: 用于與代碼審查系統(如 Gerrit)交互,提交代碼變更和查看審查結果。
- fetch: 用于下載 Chromium 項目的源代碼。
- gerrit: 用于代碼審查和版本控制。
- roll-dep: 用于管理 Chromium 項目的依賴關系。
除了這些核心工具之外,depot_tools 還包含一些其他的輔助工具,例如:
- python: 用于執行一些腳本和命令。
- gn: 用于生成構建文件。
- ninja: 用于執行構建過程。
depot_tools 被廣泛用于 Chromium 項目的開發,它能夠簡化開發流程,提高開發效率。對于 Chromium 開發者來說,掌握 depot_tools 是非常重要的。
總結來說,depot_tools 是一個專門為 Chromium 項目開發的工具集,它包含了各種工具來管理代碼倉庫、代碼審查和構建過程,是 Chromium 開發者必不可少的工具。
1.2?下載v8源碼
mkdir v8
cd v8
fetch v8
cd v8
然后就可以拉下來源碼了,如果需要配置git賬號的話可以自己查閱資料,這里不過多贅述。
1.3?v8構建
# 我是MacOS Mx的機器,所以是arm64的,不知道自己是什么target名字的話可以看gm.py文件
tools/dev/gm.py arm64.debug
我是MacOS Mx的機器,所以是arm64的,不知道自己是什么target名字的話可以看gm.py文件,搜一下x64或者arm64就可以了,然后我這里編的是debug版本的,release的話就用arm64.release。構建完成之后就會在out目錄下生成arm64.debug目錄,目錄下就是構建生成的產物,d8就是我們要是用的編譯器。
二、v8編譯器的基本使用
2.1?生成ast
function add(a, b) {return a + b;
}
add(1, 2)
對于這段javascript代碼我們可以使用如下命令打印出ast:
./d8 --print-ast add.js
生成的ast如下所示:
[generating bytecode for function: ]
--- AST ---
FUNC at 0
. KIND 0
. LITERAL ID 0
. SUSPEND COUNT 0
. NAME ""
. INFERRED NAME ""
. DECLS
. . FUNCTION "add" = function add
. EXPRESSION STATEMENT at 39
. . kAssign at -1
. . . VAR PROXY local[0] (0x13304cae8) (mode = TEMPORARY, assigned = true) ".result"
. . . CALL
. . . . VAR PROXY unallocated (0x13304c9e0) (mode = VAR, assigned = true) "add"
. . . . LITERAL 1
. . . . LITERAL 2
. RETURN at -1
. . VAR PROXY local[0] (0x13304cae8) (mode = TEMPORARY, assigned = true) ".result"[generating bytecode for function: add]
--- AST ---
FUNC at 12
. KIND 0
. LITERAL ID 1
. SUSPEND COUNT 0
. NAME "add"
. INFERRED NAME ""
. PARAMS
. . VAR (0x13304ca70) (mode = VAR, assigned = false) "a"
. . VAR (0x13304caf0) (mode = VAR, assigned = false) "b"
. DECLS
. . VARIABLE (0x13304ca70) (mode = VAR, assigned = false) "a"
. . VARIABLE (0x13304caf0) (mode = VAR, assigned = false) "b"
. RETURN at 23
. . kAdd at 32
. . . VAR PROXY parameter[0] (0x13304ca70) (mode = VAR, assigned = false) "a"
. . . VAR PROXY parameter[1] (0x13304caf0) (mode = VAR, assigned = false) "b"
上邊是個匿名函數,相當于javascript的啟動函數,我們能看到對于add函數的call調用,以及參數1和參數2。下邊就是add函數我們能看到a參數和b參數。
v8優化解析速度的一個策略就是懶解析技術,如果我們在add.js中將add函數的調用給刪掉,我們再打印ast能夠看到幾乎生成了一個空的入口函數,以及根本就沒有add函數了,這是v8優化解析速度的一個策略。
2.2 編譯字節碼
./d8 --print-bytecode add.js
使用上述命令能夠打印出字節碼,下邊是生成的add函數的字節碼:
[generated bytecode for function: add (0x3b3c00298591 <SharedFunctionInfo add>)]
Bytecode length: 6
Parameter count 3
Register count 0
Frame size 00x31230004008c @ 0 : 0b 04 Ldar a10x31230004008e @ 2 : 3b 03 00 Add a0, [0]0x312300040091 @ 5 : af Return
Constant pool (size = 0)
Handler Table (size = 0)
Source Position Table (size = 0)
?首先把參數1加載到累加寄存器上,然后把參數0加到累加器上,最后返回。
2.3 打印匯編代碼
- --trace-opt-verbose:?extra verbose optimized compilation tracing
- --trace-turbo:?race generated TurboFan IR
- --print-code:?print generated code
- --print-opt-code:?print optimized code
- --code-comments:?emit comments in code disassembly; for more readable source positions you should add --no-concurrent_recompilation
使用這些選項能夠打印出匯編,以及turbo的優化記錄,d8的選項很全的,對于開發人員來說是非常友好的,大家可以自行挖掘。
三、v8編譯器的IR可視化工具turbolizer
3.1 turbolizer構建
這個可視化工具在tools/turbolizer目錄下,第一次使用我們需要自己構建,查看README就能看到怎么構建:
cd tools/turbolizer
npm i
npm run-script build
這里的構建依賴一個npm的工具,我們需要先安裝npm,npm在Node.js的安裝包中:
# download and install Node.js
brew install node@20
# verifies the right Node.js version is in the environment
node -v # should print `v20.15.0`
# verifies the right NPM version is in the environment
npm -v # should print `10.7.0`
這里注意配置好proxy,安裝完成之后根據提示信息配置環境變量:
echo 'export PATH="/opt/homebrew/opt/node@20/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
export LDFLAGS="-L/opt/homebrew/opt/node@20/lib"
export CPPFLAGS="-I/opt/homebrew/opt/node@20/include"
都成功之后我們npm -v應該能正確打印出版本號了,然后就可以回到第一步構建turbolizer了,速度很快。
3.2 npm簡介
npm 是?Node Package Manager?的縮寫,中文意思是?節點包管理器。它是 JavaScript 生態系統中最重要的工具之一,幫助開發者管理和共享 JavaScript 代碼。
npm 有兩個主要功能:
-
包管理:?npm 允許開發者從一個龐大的公開庫中下載并安裝其他開發者編寫的代碼包(package),這些包可以提供各種各樣的功能,例如:
- 網絡請求庫(如 axios)
- 數據處理庫(如 lodash)
- 測試框架(如 jest)
- 等等。
使用 npm,開發者可以輕松地將這些代碼包添加到自己的項目中,而無需自己編寫所有的代碼。
-
包發布:?npm 也允許開發者將自己編寫的代碼包發布到 npm 的公共庫中,以便其他開發者可以下載和使用。
總而言之,npm 是 JavaScript 開發者不可或缺的工具,它簡化了代碼管理和共享,極大地提高了開發效率。
一些常用的 npm 命令:
npm install
: 安裝包npm uninstall
: 卸載包npm update
: 更新包npm search
: 搜索包npm publish
: 發布包
3.3 turbolizer使用
# python2
python2 -m SimpleHTTPServer 8000
# python3
python3 -m http.server 8000
對于不同的python版本用對應的命令構建窗口,然后在瀏覽器中打開0.0.0.0:8000(注意要關閉tizi):
我們2.3的命令能夠在目錄下生成一個json文件,然后在頁面中按Ctrl+L可以選擇打開文件:
左邊是程序代碼,中間是javascript的IR(Sea of Node),右側是匯編代碼。中間一欄的左上角還可以選擇pass,看每個優化之后的IR結構:
四、總結
這一節沒有什么干貨,就是記錄一下v8編譯器構建過程中遇到的坑,我是剛接觸v8編譯器,第一感覺是v8編譯器對于程序員來說十分友好,非常方便調試。再有一個感觸就是Sea of Node這種IR結構很強大,很簡潔,對于死代碼消除等十分方便。