1. 赋值指令
Lua中的赋值指令有如下几个:
- OP_MOVE A B R(A) := R(B)
- OP_LOADK A Bx R(A) := Kst(Bx)
- OP_LOADKX A R(A) := Kst(extra arg)
- OP_LOADBOOL A B C R(A) := (Bool)B; if (C) pc++
- OP_LOADNIL A B R(A), R(A+1), …, R(A+B) := nil
- OP_GETUPVAL A B R(A) := UpValue[B]
- OP_GETTABUP A B C R(A) := UpValue[B][RK(C)]
- OP_GETTABLE A B C R(A) := R(B)[RK(C)]
- OP_SETTABUP A B C UpValue[A][RK(B)] := RK(C)
- OP_SETUPVAL A B UpValue[B] := R(A)
- OP_SETTABLE A B C R(A)[RK(B)] := RK(C)
下面会选择其中几个指令进行分析:
2. OP_MOVE:
vmcase(OP_MOVE) {
setobjs2s(L, ra, RB(i));
vmbreak;
}
首先看RB、check_exp、GET_OPCODE、GETARG_B宏
#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
#define check_exp(c,e) (lua_assert(c), (e))
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
#define GETARG_B(i) getarg(i, POS_B, SIZE_B)
可见check_exp宏其实是用来断言检测的,getBMode是用来获取指令的B参数格式,配合check_exp来进行判断指令格式是否合法。
最重要的是base+GETARG_B(i):
- base记录的是栈基址,也就是函数栈的起始位置,在这里表示寄存器的起始地址。
- GETARG_B(i),对于OP_MOVE指令来说,B参数表示一个寄存器的索引,而GETARG_B(i)就是获取这个索引值
- base+GETARG_B(i),获取到B参数对应寄存器的值。
然后再看setobjs2s相关的代码
#define setobjs2s setobj
#define setobj(L,obj1,obj2) \
{ TValue *io1=(obj1); *io1 = *(obj2); \
(void)L; checkliveness(L,io1); }
可以看出setobjs2s(L, ra, RB(i)),就是将RB(i)寄存器的值赋值给ra,那么ra又是什么呢?其实在vmfetch中进行了初始化:
/* fetch an instruction and prepare its execution */
#define vmfetch() { \
i = *(ci->u.l.savedpc++); \
if (L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) \
Protect(luaG_traceexec(L)); \
ra = RA(i); /* WARNING: any stack reallocation invalidates 'ra' */ \
lua_assert(base == ci->u.l.base); \
lua_assert(base <= L->top && L->top < L->stack + L->stacksize); \
}
可以看到,ra就是RA(i)寄存器中的值(和RB同理)。
那么OP_MOVE指令就是将B寄存器的值赋值给A寄存器。我们可以看下如下代码编译后的字节码:
local a = 1
local b = a
main <mylua.lua:0,0> (3 instructions at 0083A2B0)
0+ params, 2 slots, 1 upvalue, 2 locals, 1 constant, 0 functions
1 [1] LOADK 0 -1 ; 1
2 [2] MOVE 1 0
3 [2] RETURN 0 1
constants (1) for 0083A2B0:
1 1
locals (2) for 0083A2B0:
0 a 2 4
1 b 3 4
upvalues (1) for 0083A2B0:
0 _ENV 1 0
可以看到,局部变量a存放在0号寄存器上,b存放在1号寄存器上,PC[1]指令表示将0号寄存器的值赋值给1号寄存器;也就是 b = a。
3. OP_LOADK:
vmcase(OP_LOADK) {
TValue *rb = k + GETARG_Bx(i);
setobj2s(L, ra, rb);
vmbreak