第一篇對Luajit做一個大概的介紹,我目前也正在慢慢的讀通源碼中,以后發現了新東西就補充在這里。
大家可以從官網下載到源碼(http://luajit.org/),也可以從Github(https://github.com/LuaDist/luajit)down下來,順便還可以看下commit記錄。
大家對著luajit的wiki結合源碼看的話會更好些,因為。。文檔太特么少了!!
目錄結構:
-- src
-- host
-- jit
*.c
*.h
*.dasc
等等,別的不是很重要
最開始我是從main函數開始看的,然后。。碰了一鼻子灰,后來研究下他的makefile,發現他是這樣子的編譯的,貼一下關鍵的msvcbuild.bat的代碼(這個更容易看懂)
1 :X64
2 minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h vm_x86.dasc
3 @if errorlevel 1 goto :BAD
4
5 %LJCOMPILE% /I "." /I %DASMDIR% host\buildvm*.c
6 @if errorlevel 1 goto :BAD
7 %LJLINK% /out:buildvm.exe buildvm*.obj
8 @if errorlevel 1 goto :BAD
9 if exist buildvm.exe.manifest^
10 %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe
11
12 buildvm -m peobj -o lj_vm.obj
13 @if errorlevel 1 goto :BAD
14 buildvm -m bcdef -o lj_bcdef.h %ALL_LIB%
15 @if errorlevel 1 goto :BAD
16 buildvm -m ffdef -o lj_ffdef.h %ALL_LIB%
17 @if errorlevel 1 goto :BAD
18 buildvm -m libdef -o lj_libdef.h %ALL_LIB%
19 @if errorlevel 1 goto :BAD
20 buildvm -m recdef -o lj_recdef.h %ALL_LIB%
21 @if errorlevel 1 goto :BAD
22 buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB%
23 @if errorlevel 1 goto :BAD
24 buildvm -m folddef -o lj_folddef.h lj_opt_fold.c
25 @if errorlevel 1 goto :BAD
2 minilua %DASM% -LN %DASMFLAGS% -o host\buildvm_arch.h vm_x86.dasc
3 @if errorlevel 1 goto :BAD
4
5 %LJCOMPILE% /I "." /I %DASMDIR% host\buildvm*.c
6 @if errorlevel 1 goto :BAD
7 %LJLINK% /out:buildvm.exe buildvm*.obj
8 @if errorlevel 1 goto :BAD
9 if exist buildvm.exe.manifest^
10 %LJMT% -manifest buildvm.exe.manifest -outputresource:buildvm.exe
11
12 buildvm -m peobj -o lj_vm.obj
13 @if errorlevel 1 goto :BAD
14 buildvm -m bcdef -o lj_bcdef.h %ALL_LIB%
15 @if errorlevel 1 goto :BAD
16 buildvm -m ffdef -o lj_ffdef.h %ALL_LIB%
17 @if errorlevel 1 goto :BAD
18 buildvm -m libdef -o lj_libdef.h %ALL_LIB%
19 @if errorlevel 1 goto :BAD
20 buildvm -m recdef -o lj_recdef.h %ALL_LIB%
21 @if errorlevel 1 goto :BAD
22 buildvm -m vmdef -o jit\vmdef.lua %ALL_LIB%
23 @if errorlevel 1 goto :BAD
24 buildvm -m folddef -o lj_folddef.h lj_opt_fold.c
25 @if errorlevel 1 goto :BAD
先創建了一個buildvm.exe的中間工具,來自動生成代碼,分別生成了lj_vm.obj,lj_bcdef.h,lj_ffdef.h ,lj_recdef.h ,jit\vmdef.lua,lj_folddef.h, lj_libdef.h
其中lv_vm.obj是依賴于host\buildvm_arch.h的,這個是用DynASM預處理vm_x86.dasc生成的,這個工具的具體分析會在下一篇博客提及。
先來看下上面自動生成的代碼:
lj_bcdef.h:
1 LJ_DATADEF const uint16_t lj_bc_ofs[] = {
2 0,
3 71,
4 142,
5 213,
6 284,
7
8 };
9
10 LJ_DATADEF const uint16_t lj_bc_mode[] = {
11 BCDEF(BCMODE)
12 BCMODE_FF,
13 BCMODE_FF,
14 BCMODE_FF,
15 BCMODE_FF,
16 BCMODE_FF,
17
18 };
2 0,
3 71,
4 142,
5 213,
6 284,
7

8 };
9
10 LJ_DATADEF const uint16_t lj_bc_mode[] = {
11 BCDEF(BCMODE)
12 BCMODE_FF,
13 BCMODE_FF,
14 BCMODE_FF,
15 BCMODE_FF,
16 BCMODE_FF,
17

18 };
lj_bc_ofs[]可能是bc在vm代碼段中的偏移量(這個我還沒深入進去調試一下),vm的一部分是用DynASM直接擼匯編擼出來的,wiki中也有提到下一步jit化的opcode等等。
lj_bc_mode[]的用來根據壓縮后的bytecode構造,分離出操作數,第一行的兩個宏的定義是
#define BCMODE(name, ma, mb, mc, mm) \
(BCM##ma|(BCM##mb<<3)|(BCM##mc<<7)|(MM_##mm<<11)),
#define BCMODE_FF 0
#define BCDEF(_) \
/* Comparison ops. ORDER OPR. */ \
_(ISLT, var, ___, var, lt) \
_(ISGE, var, ___, var, lt) \
_(ISLE, var, ___, var, le) \
_(ISGT, var, ___, var, le) \
...
總之就是充斥著各種拼接起來的宏
lj_ffdef.h:
vmdef.lua:
這個里面內容就不貼了,包括bcname,irname,irfpm,irfield,ircall 的定義,在jit文件夾下面,用于調試等,比如在dump.lua中就有用到
當你用luajit -jdump的時候,就是調用的lua的jit庫里面的lua函數
lj_recdef.h:
recff_idmap[]被用在lj_ffrecord_func這個函數中,有一個關鍵的數據結構RecordFFData,用來記錄在trace過程中被調用函數的參數和返回值個數,和一些輔助數據,opcode,literal等等。通過recff_idmap[]保存的值來區分函數(待仔細研究)
lj_folddef.h:
(這個Optimation略復雜,以后的博文中再說)
----------------------------------------分割線-------------------------------------------
以上就是buildvm生成代碼,在很多.c的文件中,他加入了一些無意義的MARCO,目的是為了能被buildvm識別出
下面說說src根目錄下面的文件:
lauxlib.h:
用戶開發擴展和與C交互的時候的頭文件
lib_*.h /.c:
顧名思義,就是利用LuaAPI寫的內部標準庫,會在方法上表明是否會被trace ( LJLIB_REC(.) )。
ljamalg.c:
文件的合并
lj_alloc.h /.c:
定制的Memory Allocator
lj_api.c:
lj_arch.h:
lj_asm.h/ .c lj_asm_*.c lj_emit_*.h lj_target_*.h/.c :
將IR編譯成Machine Code,關鍵的數據結構ASMState,線性掃描的O(n2)分配算法
lj_bc.h/ .c:
Luajit字節碼的定義和內存布局
lj_bcdump.c lj_bcread.c lj_bcwrite.c:
圍繞著字節碼的操作
lj_carith.c:
lj_ccall.h/ .c lj_ccallback.h / .c :
FFI C語言函數調用和回調綁定
lj_debug.h/.c :
調試與自省用
lj_def.h:
這個很重要,重要的類型和一些宏定義在這里
lj_c*.h/ .c:
和C語言先關的,比如類型轉化,char管理,數據管理
lj_frame.h:
Luajit的棧幀管理
lj_func.h/.c:
Function handle和閉包有關的upvalue數據結構
lj_gc.h/.c:
GC相關,GC可以看下luajit的wiki,里面涉及不少增量式GC的paper和作者的看法
lj_gdbjit.h/.c :
對gdb的支持
lj_ir*.h/.c:
SSA,IR相關(這個和bytecode還是不一樣的)操作和優化
lj_lex.h/.c lj_parse.h/.c:
lexer和parser
lj_mcode.h/.c:
Machine Code管理
lj_opt_*.h:
各種bytecode層面上的優化
lj_snap.h/.c:
快照支持
lj_state.h/.c:
LuaState和Stack的操作
lj_str*.h/.c lj_tab.h/.c:
原生類型string和table操作
lj_udata.h/.c:
類型user data的操作
lj_vm.h/.c lj_vmevent.h/.c:
vm的API和事件注冊(lj_vmevent_send)
lj_vmmath.h/.c:
對vm支持的math庫
lua.h:
luaState等基本的Lua結構
lualib.h:
和Lua一樣,標準庫的API
luajit.h:
luajit 的public API
vm_*.dasc:
編譯期被DynASM預處理的源文件,下一篇講DynASM時候介紹dasc文件
wmain.c:
windows下面的main入口
和Trace相關的:
lj_crecord.h/.c : C操作的trace record
lj_dispatch.h/.c : 指令分發,調用ASMFuction,處理指令前的hook和記錄trace用的hot count,有一個重要的數據結構 GG_State
lj_ff*.h/.c: 上面講lj_ffdef.h的時候提過,trace的時候 記錄Fast Function的調用記數
lj_trace.h/.c: trace的具體過程
lj_traceerr.h : trace error
...
總之就是充斥著各種拼接起來的宏
lj_ffdef.h:
1 FFDEF(assert)
2 FFDEF(type)
3 FFDEF(next)
4 FFDEF(pairs)
5 FFDEF(ipairs_aux)
6
FFDEF的定義是在2 FFDEF(type)
3 FFDEF(next)
4 FFDEF(pairs)
5 FFDEF(ipairs_aux)
6

1 /* Fast function ID. */
2 typedef enum {
3 FF_LUA_ = FF_LUA, /* Lua function (must be 0). */
4 FF_C_ = FF_C, /* Regular C function (must be 1). */
5 #define FFDEF(name) FF_##name,
6 #include "lj_ffdef.h"
7 FF__MAX
8 } FastFunc;
差不多就是用FF_##name把上面的名字拼接起來,然后生成在enum里面,這樣就能當成是數字,在數組中迅速找到入口了2 typedef enum {
3 FF_LUA_ = FF_LUA, /* Lua function (must be 0). */
4 FF_C_ = FF_C, /* Regular C function (must be 1). */
5 #define FFDEF(name) FF_##name,
6 #include "lj_ffdef.h"
7 FF__MAX
8 } FastFunc;
vmdef.lua:
這個里面內容就不貼了,包括bcname,irname,irfpm,irfield,ircall 的定義,在jit文件夾下面,用于調試等,比如在dump.lua中就有用到
local jit = require("jit")
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef") // ← ← ← ←
assert(jit.version_num == 20002, "LuaJIT core/library version mismatch")
local jutil = require("jit.util")
local vmdef = require("jit.vmdef") // ← ← ← ←
當你用luajit -jdump的時候,就是調用的lua的jit庫里面的lua函數
lj_recdef.h:
1 static const uint16_t recff_idmap[] = {
2 0,
3 0x0100,
4 0x0200,
5 0x0300,
6 0,
7 0,
8 0x0400,
9
10 };
11
12 static const RecordFunc recff_func[] = {
13 recff_nyi,
14 recff_c,
15 recff_assert,
16 recff_type,
17 recff_ipairs_aux,
18
19 };
其中recff_func[]是被注冊的被traced jit 跟蹤的函數,具體可是在lj_ffrecord.c里面看到2 0,
3 0x0100,
4 0x0200,
5 0x0300,
6 0,
7 0,
8 0x0400,
9

10 };
11
12 static const RecordFunc recff_func[] = {
13 recff_nyi,
14 recff_c,
15 recff_assert,
16 recff_type,
17 recff_ipairs_aux,
18

19 };
recff_idmap[]被用在lj_ffrecord_func這個函數中,有一個關鍵的數據結構RecordFFData,用來記錄在trace過程中被調用函數的參數和返回值個數,和一些輔助數據,opcode,literal等等。通過recff_idmap[]保存的值來區分函數(待仔細研究)
lj_folddef.h:
1 static const FoldFunc fold_func[] = {
2 fold_kfold_numarith,
3 fold_kfold_ldexp,
4 fold_kfold_fpmath,
5 fold_kfold_numpow,
6
7 };
8
9 static const uint32_t fold_hash[916] = {
10 0xffffffff,
11 0xffffffff,
12 0x5b4c8016,
13
14 };
用在FOLD optimization中,見lj_opt_fold.c,主要在2 fold_kfold_numarith,
3 fold_kfold_ldexp,
4 fold_kfold_fpmath,
5 fold_kfold_numpow,
6

7 };
8
9 static const uint32_t fold_hash[916] = {
10 0xffffffff,
11 0xffffffff,
12 0x5b4c8016,
13

14 };
1 if ((fh & 0xffffff) == k || (fh = fold_hash[h+1], (fh & 0xffffff) == k)) {
2 ref = (IRRef)tref_ref(fold_func[fh >> 24](J));
3 if (ref != NEXTFOLD)
4 break;
5 }
是根據數組偏移獲取函數,直接執行。2 ref = (IRRef)tref_ref(fold_func[fh >> 24](J));
3 if (ref != NEXTFOLD)
4 break;
5 }
(這個Optimation略復雜,以后的博文中再說)
----------------------------------------分割線-------------------------------------------
以上就是buildvm生成代碼,在很多.c的文件中,他加入了一些無意義的MARCO,目的是為了能被buildvm識別出
下面說說src根目錄下面的文件:
lauxlib.h:
用戶開發擴展和與C交互的時候的頭文件
lib_*.h /.c:
顧名思義,就是利用LuaAPI寫的內部標準庫,會在方法上表明是否會被trace ( LJLIB_REC(.) )。
ljamalg.c:
文件的合并
lj_alloc.h /.c:
定制的Memory Allocator
lj_api.c:
Public Lua/C API.
lj_arch.h:
Target architecture selection
lj_jit.h:
jit編譯器里面數據結構的定義
lj_jit.h:
jit編譯器里面數據結構的定義
lj_asm.h/ .c lj_asm_*.c lj_emit_*.h lj_target_*.h/.c :
將IR編譯成Machine Code,關鍵的數據結構ASMState,線性掃描的O(n2)分配算法
lj_bc.h/ .c:
Luajit字節碼的定義和內存布局
lj_bcdump.c lj_bcread.c lj_bcwrite.c:
圍繞著字節碼的操作
lj_carith.c:
C實現的一些數字運算
lj_ccall.h/ .c lj_ccallback.h / .c :
FFI C語言函數調用和回調綁定
lj_debug.h/.c :
調試與自省用
lj_def.h:
這個很重要,重要的類型和一些宏定義在這里
lj_c*.h/ .c:
和C語言先關的,比如類型轉化,char管理,數據管理
lj_frame.h:
Luajit的棧幀管理
lj_func.h/.c:
Function handle和閉包有關的upvalue數據結構
lj_gc.h/.c:
GC相關,GC可以看下luajit的wiki,里面涉及不少增量式GC的paper和作者的看法
lj_gdbjit.h/.c :
對gdb的支持
lj_ir*.h/.c:
SSA,IR相關(這個和bytecode還是不一樣的)操作和優化
lj_lex.h/.c lj_parse.h/.c:
lexer和parser
lj_mcode.h/.c:
Machine Code管理
lj_opt_*.h:
各種bytecode層面上的優化
lj_snap.h/.c:
快照支持
lj_state.h/.c:
LuaState和Stack的操作
lj_str*.h/.c lj_tab.h/.c:
原生類型string和table操作
lj_udata.h/.c:
類型user data的操作
lj_vm.h/.c lj_vmevent.h/.c:
vm的API和事件注冊(lj_vmevent_send)
lj_vmmath.h/.c:
對vm支持的math庫
lua.h:
luaState等基本的Lua結構
lualib.h:
和Lua一樣,標準庫的API
luajit.h:
luajit 的public API
vm_*.dasc:
編譯期被DynASM預處理的源文件,下一篇講DynASM時候介紹dasc文件
wmain.c:
windows下面的main入口
和Trace相關的:
lj_crecord.h/.c : C操作的trace record
lj_dispatch.h/.c : 指令分發,調用ASMFuction,處理指令前的hook和記錄trace用的hot count,有一個重要的數據結構 GG_State
lj_ff*.h/.c: 上面講lj_ffdef.h的時候提過,trace的時候 記錄Fast Function的調用記數
lj_trace.h/.c: trace的具體過程
lj_traceerr.h : trace error