2022 再见~ 今天以一篇小文章来结束 2022 啦。这一次的文章篇幅很短,只是一点开胃小菜,简单分析下 Lua 的 opcode 指令结构与 luaV_execute
函数。
指令结构
Lua 有三类结构的指令,分别为 iABC
、iABx
与 iAsBx
,三者的示意图如上。绝大多数指令都是 iABC
型的,只有少量 iABx
与 iAsBx
。其中,iABx
的典型指令如 OP_LOADK
、OP_GETGLOBAL
等,iAsBx
的典型指令如 OP_JMP
、OP_FORLOP
等。
三种结构的指令里,opcode
的部分都固定为 6 bits,也就是说 Lua 最多只有 64 种指令。剩下的几个参数,分为 B
、Bx
、sBx
三种,第一种一般是栈上相对于 base
指针的下标(寄存器下标),第二种一般是一些在 Kst
表里的下标或立即数,第三种则是第二种的带符号版本。
sBx
形式的数字并不是正常的带符号数字表示法,而是相对于K
的偏置值。K
是该参数的最大可取值。因此当sBx
等于0
时,其实表示着-K
,当sBx
等于K
时,其实表示着2*K
。
几个参数的寻址方式共有三类:
OpArgR
+R
类:对应RA
、RB
、RC
等宏,寻址方式为base + R
OpArgK
+R
类:对应RKB
、RKC
等宏,寻址方式为ISK(R) ? k + INDEXK(R) : base + R
OpArgK
+Rx
类:对应KBx
等宏,寻址方式为k + Rx
在第二种场景下,R
中的取值类型由最高位决定。如果最高位为 0
,代表着是寄存器下标;如果最高位为 1
,代表着是立即数(这也是 ISK
这个宏的含义)。而 INDEXK
宏的作用就是除去最高位。
几个相关的指针/表
StkId base = L->base
: 栈基,供传递函数参数、局部变量等;TValue *k = cl->p->k
: 常量表,在解析源码过程中产生,放置一些常数、常量等;TValue g = *cl->env
: 全局变量表,顾名思义,放置一些全局的变量;UpVal * cl->upvals
: upval 表,在创建 closure 时往下层传递,后边分析OP_CLOSURE
时再仔细看看;
luaV_execute
函数
void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *cl;
StkId base;
TValue *k;
const Instruction *pc;
reentry: /* entry point */
lua_assert(isLua(L->ci));
// 当前指令
// 这个 L->savedpc 在 luaD_precall 中已设置为 p->code,也就是语法解析获得的指令序列,如下:
// // 将 pc 指向解析出来的 Proto->code,也就是 opcode 数组
// L->savedpc = p->code; /* starting point */
pc = L->savedpc;
// 当前 CallInfo 对应的闭包
cl = &clvalue(L->ci->func)->l;
// 栈基
base = L->base;
// 常量表,注意到此表随着 closure 一起管理
k = cl->p->k;
/* main loop of interpreter */
for (;;) {
// 取指令并自增
const Instruction i = *pc++;
StkId ra;
if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
(--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
traceexec(L, pc);
if (L->status == LUA_YIELD) { /* did hook yield? */
L->savedpc = pc - 1;
return;
}
base = L->base;
}
/* warning!! several calls may realloc the stack and invalidate `ra' */
ra = RA(i);
lua_assert(base == L->base && L->base == L->ci->base);
lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);
lua_assert(L->top == L->ci->top || luaG_checkopenop(i));
switch (GET_OPCODE(i)) {
// ...
opcode 一览
typedef enum {
// 注:为了方便分组,opcode 的顺序被我做了调换
// 装载
OP_MOVE,/* A B R(A) := R(B) */
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
// 基本运算
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM,/* A B R(A) := -R(B) */
OP_NOT,/* A B R(A) := not R(B) */
OP_LEN,/* A B R(A) := length of R(B) */
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
// 条件、循环、分支跳转
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_JMP,/* sBx pc+=sBx */
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
// 函数
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
// 表
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
} OpCode;
题图来自 Photo by Joshua Rodriguez on Unsplash