lua symbexec中的跳转和setlist

 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;
        }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值