在llimits.h文件中定義了指令的類型。其實就是32個字節。
typedef lu_int32 Instruction;
上節說到變量最終會存入proto的數組k中,返回的索引放在expdesc *var->u.s.info。那么這個索引就是用來生成中間碼的指令。如下。
int e = luaK_exp2anyreg(fs, ex);//返回寄存器索引 luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);//生成指令
首先講一下lua指令的構造,如圖。簡單來說就是把32位字節分割一下,低位的6個字節表示的是哪一種操作指令, 即opcode; 后面的分為2個參數和三個參數的情況。參數代表的計算在寄存器中的索引,寄存器可以想象成一個數組。
例如,如果指令的三段分別代表,“add 0 1”.表示的意思是對兩個數求和,兩個數分別在寄存器中,索引為0 和 1 的地方。
lopcodes.h定義了相關方法。其實就是些二進制的操作,可以獲取指定范圍內的比特值。不熟悉的同學可以先學習C語言的二進制運算。
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
lopcodes.c中定義了相關參數的比特長度:
enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ /* ** size and position of opcode arguments. */ #define SIZE_C 9 #define SIZE_B 9 #define SIZE_Bx (SIZE_C + SIZE_B) #define SIZE_A 8 #define SIZE_OP 6 #define POS_OP 0 #define POS_A (POS_OP + SIZE_OP) #define POS_C (POS_A + SIZE_A) #define POS_B (POS_C + SIZE_C) #define POS_Bx POS_C
生成指令時,主要依據以下的格式:
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) const lu_byte luaP_opmodes[NUM_OPCODES] = { /* T A B C mode opcode */ opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
......
}
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
生成指令后,那么寄存器的地址和函數類型proto是如何對應的呢,如下圖
至此,生成中間碼指令的過程就分析完了。下一步重點關注lua本身的數據結構。
done。