static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
int pc;
int last; /* stores position of last instruction that changed `reg' */
last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
check(precheck(pt));
for (pc = 0; pc < lastpc; pc++) {
Instruction i = pt->code[pc];
OpCode op = GET_OPCODE(i);
int a = GETARG_A(i);
int b = 0;
int c = 0;
check(op < NUM_OPCODES);
checkreg(pt, a);
switch (getOpMode(op)) {
case iABC: {
b = GETARG_B(i);
c = GETARG_C(i);
check(checkArgMode(pt, b, getBMode(op)));
check(checkArgMode(pt, c, getCMode(op)));
break;
}
case iABx: {
b = GETARG_Bx(i);
if (getBMode(op) == OpArgK) check(b < pt->sizek);
break;
}
case iAsBx: {
b = GETARG_sBx(i);
if (getBMode(op) == OpArgR) {
int dest = pc+1+b;
check(0 <= dest && dest < pt->sizecode);
if (dest > 0) {
int j;
/* check that it does not jump to a setlist count; this
is tricky, because the count from a previous setlist may
have the same value of an invalid setlist; so, we must
go all the way back to the first of them (if any) */
for (j = 0; j < dest; j++) {
Instruction d = pt->code[dest-1-j];
if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
}
/* if 'j' is even, previous value is not a setlist (even if
it looks like one) */
check((j&1) == 0);
}
}
break;
}
}
if (testAMode(op)) {
if (a == reg) last = pc; /* change register `a' */
}
if (testTMode(op)) {
check(pc+2 < pt->sizecode); /* check skip */
check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);
}
switch (op) {
case OP_LOADBOOL: {
if (c == 1) { /* does it jump? */
check(pc+2 < pt->sizecode); /* check its jump */
check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
GETARG_C(pt->code[pc+1]) != 0);
}
break;
}
case OP_LOADNIL: {
if (a <= reg && reg <= b)
last = pc; /* set registers from `a' to `b' */
break;
}
case OP_GETUPVAL:
case OP_SETUPVAL: {
check(b < pt->nups);
break;
}
case OP_GETGLOBAL:
case OP_SETGLOBAL: {
check(ttisstring(&pt->k[b]));
break;
}
case OP_SELF: {
checkreg(pt, a+1);
if (reg == a+1) last = pc;
break;
}
case OP_CONCAT: {
check(b < c); /* at least two operands */
break;
}
case OP_TFORLOOP: {
check(c >= 1); /* at least one result (control variable) */
checkreg(pt, a+2+c); /* space for results */
if (reg >= a+2) last = pc; /* affect all regs above its base */
break;
}
case OP_FORLOOP:
case OP_FORPREP:
checkreg(pt, a+3);
/* go through */
case OP_JMP: {
int dest = pc+1+b;
/* not full check and jump is forward and do not skip `lastpc'? */
if (reg != NO_REG && pc < dest && dest <= lastpc)
pc += b; /* do the jump */
break;
}
case OP_CALL:
case OP_TAILCALL: {
if (b != 0) {
checkreg(pt, a+b-1);
}
c--; /* c = num. returns */
if (c == LUA_MULTRET) {
check(checkopenop(pt, pc));
}
else if (c != 0)
checkreg(pt, a+c-1);
if (reg >= a) last = pc; /* affect all registers above base */
break;
}
case OP_RETURN: {
b--; /* b = num. returns */
if (b > 0) checkreg(pt, a+b-1);
break;
}
case OP_SETLIST: {
if (b > 0) checkreg(pt, a + b);
if (c == 0) {
pc++;
check(pc < pt->sizecode - 1);
}
break;
}
case OP_CLOSURE: {
int nup, j;
check(b < pt->sizep);
nup = pt->p[b]->nups;
check(pc + nup < pt->sizecode);
for (j = 1; j <= nup; j++) {
OpCode op1 = GET_OPCODE(pt->code[pc + j]);
check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
}
if (reg != NO_REG) /* tracing? */
pc += nup; /* do not 'execute' these pseudo-instructions */
break;
}
case OP_VARARG: {
check((pt->is_vararg & VARARG_ISVARARG) &&
!(pt->is_vararg & VARARG_NEEDSARG));
b--;
if (b == LUA_MULTRET) check(checkopenop(pt, pc));
checkreg(pt, a+b-1);
break;
}
default: break;
}
}
return pt->code[last];
}
先前一直不明白ABx组合的指令为什么和setlist扯上关系了(case iAsBx 部分)。仔细看talbe的实现才恍然大悟。
首先setlist是用来设置table的数组部分的,形容local tb = {"a", "b", "c", "d"}, 在解析这段lua代码时,并不为每一个数组成员生成一条设置指令,而是把数组成员进行压栈,待到一定数量时(LFIELDS_PER_FLUSH:50)写入一次setlist指令。setlist指令的A操作数为table在栈上的下一个位置,B操作数为个数, 当构造一个大于50个的表时,由于解析50个就setlist一次所以下个setlist就需要在指令中保存本次数组第一个成员的位置,这个数字保存在C操作数里头。C的值为 int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
对于100个成员来说,有两次setlist, 第一次setlist的指令发生在满50时,整条指令形如 OP_SETLIST talbe next pos, 50, (50 - 1) / 50 + 1, 第二次setlist的指令发生在满100时,整条指令形如 OP_SETLIST talbe next pos, 50, (100 - 1) / 50 + 1. 也即第一个setlist的C操作数为1,第二个为2. 在虚拟机执行的时候根据((c-1)*LFIELDS_PER_FLUSH就能算出本次setlist的第一个成员应该置table数组部分的那个位置, (再根据B和A也即能知道本次setlist的数据从栈的哪里取得)
那么问题来了,因为C操作数只有可怜的9位,最多存入512,当数组部分成员大于 25551是,C操作数就容纳不下了。 肿么办呢? lua的做法是当插入过多的成员,超过C所能容纳的,就把把C重置为0,而下条指令(32位)用来存储该数值。可以看出PC链中也不全为指令,OP_SETLIST指令当C位为0时后面还跟着一条不是指令的指令。既然它不是指令,又被放入指令链中,它就有可能长得像一些指令,毕竟正常的指令也是32位的。
至于case iAsBx片段就是为了判断是否跳转到了setlist的count部分,也就是那条非指令的指令。
case iAsBx: {
b = GETARG_sBx(i); // 取出要跳转到的目的地
if (getBMode(op) == OpArgR) {
int dest = pc+1+b; // 目的地
check(0 <= dest && dest < pt->sizecode);
if (dest > 0) {
int j;
/* 从目的地前一个位置开始, 如果该位置是setlist,且C操作数为0,则说明目的地有可能
是SetList的另一半非指令的指令。但是这还无法确定,因为非指令的指令是一个32位的数值,正常的
指令也是32位,他有可能刚好就长得像setlist且C为0,由于不可能有两条非指令的指令连在一次,
且setlist都是连一起所以可以往上继续判断,直到找到C非0的SETLIST,如果这条指令在偶数,
目的地之上的Setlist就已经完成配对了,所以目的地不可能是那条非指令的指令了。
*/
for (j = 0; j < dest; j++) {
Instruction d = pt->code[dest-1-j];
if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
}
/* if 'j' is even, previous value is not a setlist (even if
it looks like one) */
check((j&1) == 0);
}
}
break;
}