2
0
Эх сурвалжийг харах

simpler implementation for `for' loops

Roberto Ierusalimschy 23 жил өмнө
parent
commit
38b0e6128d
7 өөрчлөгдсөн 57 нэмэгдсэн , 95 устгасан
  1. 0 11
      lcode.c
  2. 0 1
      lcode.h
  3. 10 30
      ldebug.c
  4. 4 8
      lopcodes.c
  5. 2 4
      lopcodes.h
  6. 9 6
      lparser.c
  7. 32 35
      lvm.c

+ 0 - 11
lcode.c

@@ -76,17 +76,6 @@ static void luaK_fixjump (FuncState *fs, int pc, int dest) {
 }
 
 
-/*
-** prep-for instructions (OP_FORPREP & OP_TFORPREP) have a negated jump,
-** as they simulate the real jump...
-*/
-void luaK_fixfor (FuncState *fs, int pc, int dest) {
-  Instruction *jmp = &fs->f->code[pc];
-  int offset = dest-(pc+1);
-  SETARG_sBc(*jmp, -offset);
-}
-
-
 /*
 ** returns current `pc' and marks it as a jump target (to avoid wrong
 ** optimizations with consecutive instructions not in the same basic block).

+ 0 - 1
lcode.h

@@ -57,7 +57,6 @@ void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
 void luaK_setcallreturns (FuncState *fs, expdesc *var, int nresults);
 int luaK_jump (FuncState *fs);
 void luaK_patchlist (FuncState *fs, int list, int target);
-void luaK_fixfor (FuncState *fs, int pc, int dest);
 void luaK_concat (FuncState *fs, int *l1, int l2);
 int luaK_getlabel (FuncState *fs);
 void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);

+ 10 - 30
ldebug.c

@@ -54,13 +54,6 @@ LUA_API lua_Hook lua_setlinehook (lua_State *L, lua_Hook func) {
 }
 
 
-static CallInfo *ci_stack (lua_State *L, StkId obj) {
-  CallInfo *ci = L->ci;
-  while (ci->base > obj && ci > L->base_ci) ci--;
-  return ci;
-}
-
-
 LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
   int status;
   lua_lock(L);
@@ -283,6 +276,7 @@ static int checklineinfo (const Proto *pt) {
   int *lineinfo = pt->lineinfo;
   if (lineinfo == NULL) return 1;
   check(pt->sizelineinfo >= 2 && lineinfo[pt->sizelineinfo-1] == MAX_INT);
+  lua_assert(luaG_getline(lineinfo, pt->sizecode-1, 1, NULL) < MAX_INT);
   if (*lineinfo < 0) lineinfo++;
   check(*lineinfo == 0);
   return 1;
@@ -292,7 +286,7 @@ static int checklineinfo (const Proto *pt) {
 static int precheck (const Proto *pt) {
   check(checklineinfo(pt));
   check(pt->maxstacksize <= MAXSTACK);
-  check(pt->numparams+pt->is_vararg <= pt->maxstacksize);
+  lua_assert(pt->numparams+pt->is_vararg <= pt->maxstacksize);
   check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
   return 1;
 }
@@ -381,7 +375,9 @@ static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) {
         check(c < MAXSTACK && b < c);
         break;
       }
-      case OP_JMP: {
+      case OP_JMP:
+      case OP_FORLOOP:
+      case OP_TFORLOOP: {
         int dest = pc+1+b;
 	check(0 <= dest && dest < pt->sizecode);
         /* not full check and jump is forward and do not skip `lastpc'? */
@@ -407,21 +403,6 @@ static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) {
         if (b > 0) checkreg(pt, a+b-1);
         break;
       }
-      case OP_FORPREP:
-      case OP_TFORPREP: {
-        int dest = pc-b;  /* jump is negated here */
-        check(0 <= dest && dest < pt->sizecode &&
-              GET_OPCODE(pt->code[dest]) == op+1);
-        break;
-      }
-      case OP_FORLOOP:
-      case OP_TFORLOOP: {
-        int dest = pc+b;
-        check(0 <= dest && dest < pt->sizecode &&
-              pt->code[dest] == SET_OPCODE(i, op-1));
-        checkreg(pt, a + ((op == OP_FORLOOP) ? 2 : 3));
-        break;
-      }
       case OP_SETLIST: {
         checkreg(pt, a + (b&(LFIELDS_PER_FLUSH-1)) + 1);
         break;
@@ -445,12 +426,11 @@ int luaG_checkcode (const Proto *pt) {
 }
 
 
-static const char *getobjname (lua_State *L, StkId obj, const char **name) {
-  CallInfo *ci = ci_stack(L, obj);
+static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,
+                               const char **name) {
   if (isLmark(ci)) {  /* an active Lua function? */
     Proto *p = ci_func(ci)->l.p;
     int pc = currentpc(L, ci);
-    int stackpos = obj - ci->base;
     Instruction i;
     *name = luaF_getlocalname(p, stackpos+1, pc);
     if (*name)  /* is a local? */
@@ -467,7 +447,7 @@ static const char *getobjname (lua_State *L, StkId obj, const char **name) {
         int a = GETARG_A(i);
         int b = GETARG_B(i);  /* move from `b' to `a' */
         if (b < a)
-          return getobjname(L, ci->base+b, name);  /* get name for `b' */
+          return getobjname(L, ci, b, name);  /* get name for `b' */
         break;
       }
       case OP_GETTABLE:
@@ -496,7 +476,7 @@ static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
     Instruction i;
     i = p->code[pc];
     return (GET_OPCODE(i) == OP_CALL
-             ? getobjname(L, ci->base+GETARG_A(i), name)
+             ? getobjname(L, ci, GETARG_A(i), name)
              : NULL);  /* no useful name found */
   }
 }
@@ -504,7 +484,7 @@ static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
 
 void luaG_typeerror (lua_State *L, StkId o, const char *op) {
   const char *name;
-  const char *kind = getobjname(L, o, &name);
+  const char *kind = getobjname(L, L->ci, o - L->ci->base, &name);  /* ?? */
   const char *t = luaT_typenames[ttype(o)];
   if (kind)
     luaO_verror(L, "attempt to %.30s %.20s `%.40s' (a %.10s value)",

+ 4 - 8
lopcodes.c

@@ -46,9 +46,7 @@ const char *const luaP_opnames[] = {
   "TESTF",
   "CALL",
   "RETURN",
-  "FORPREP",
   "FORLOOP",
-  "TFORPREP",
   "TFORLOOP",
   "SETLIST",
   "SETLISTO",
@@ -60,10 +58,10 @@ const char *const luaP_opnames[] = {
 
 #define opmode(t,x,b,c,sa,k,m) (((t)<<OpModeT) | \
    ((b)<<OpModeBreg) | ((c)<<OpModeCreg) | \
-   ((sa)<<OpModesetA) | ((k)<<OpModeK) | (m))
+   ((sa)<<OpModesetA) | ((k)<<OpModeK) | (x)<<OpModeNoTrace | (m))
 
 const lu_byte luaP_opmodes[NUM_OPCODES] = {
-/*       T _ B C sA K mode		   opcode    */
+/*       T n B C sA K mode		   opcode    */
   opmode(0,0,1,0, 1,0,iABC)		/* OP_MOVE */
  ,opmode(0,0,0,0, 1,1,iABc)		/* OP_LOADK */
  ,opmode(0,0,0,0, 1,0,iABC)		/* OP_LOADBOOL */
@@ -95,10 +93,8 @@ const lu_byte luaP_opmodes[NUM_OPCODES] = {
  ,opmode(1,0,1,0, 1,0,iABC)		/* OP_TESTF */
  ,opmode(0,0,0,0, 0,0,iABC)		/* OP_CALL */
  ,opmode(0,0,0,0, 0,0,iABC)		/* OP_RETURN */
- ,opmode(0,0,0,0, 0,0,iAsBc)		/* OP_FORPREP */
- ,opmode(0,0,0,0, 0,0,iAsBc)		/* OP_FORLOOP */
- ,opmode(0,0,0,0, 0,0,iAsBc)		/* OP_TFORPREP */
- ,opmode(0,0,0,0, 0,0,iAsBc)		/* OP_TFORLOOP */
+ ,opmode(0,1,0,0, 0,0,iAsBc)		/* OP_FORLOOP */
+ ,opmode(0,1,0,0, 0,0,iAsBc)		/* OP_TFORLOOP */
  ,opmode(0,0,0,0, 0,0,iABc)		/* OP_SETLIST */
  ,opmode(0,0,0,0, 0,0,iABc)		/* OP_SETLISTO */
  ,opmode(0,0,0,0, 0,0,iABC)		/* OP_CLOSE */

+ 2 - 4
lopcodes.h

@@ -169,10 +169,7 @@ OP_TESTF,/*	A B	if not (R(B)) then R(A) := R(B) else pc++	*/
 OP_CALL,/*	A B C	R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1))*/
 OP_RETURN,/*	A B	return R(A), ... ,R(A+B-2)	(see (3))	*/
 
-OP_FORPREP,/*	A sBc							*/
 OP_FORLOOP,/*	A sBc							*/
-
-OP_TFORPREP,/*	A sBc							*/
 OP_TFORLOOP,/*	A sBc							*/
 
 OP_SETLIST,/*	A Bc	R(A)[Bc-Bc%FPF+i] := R(A+i), 1 <= i <= Bc%FPF+1	*/
@@ -206,7 +203,8 @@ enum OpModeMask {
   OpModeCreg,           /* C is a register/constant */
   OpModesetA,           /* instruction set register A */
   OpModeK,              /* Bc is a constant */
-  OpModeT               /* operator is a test */
+  OpModeT,		/* operator is a test */
+  OpModeNoTrace		/* operator should not be traced */
 };
 
 extern const lu_byte luaP_opmodes[NUM_OPCODES];

+ 9 - 6
lparser.c

@@ -955,17 +955,17 @@ static void exp1 (LexState *ls) {
 }
 
 
-static void forbody (LexState *ls, int nvar, OpCode prepfor, OpCode loopfor) {
+static void forbody (LexState *ls, int nvar, OpCode loopfor) {
   /* forbody -> DO block END */
   FuncState *fs = ls->fs;
   int basereg = fs->freereg - nvar;
-  int prep = luaK_codeAsBc(fs, prepfor, basereg, NO_JUMP);
+  int prep = luaK_jump(fs);
   int blockinit = luaK_getlabel(fs);
   check(ls, TK_DO);
   adjustlocalvars(ls, nvar);  /* scope for control variables */
   block(ls);
+  luaK_patchlist(fs, prep, luaK_getlabel(fs));
   luaK_patchlist(fs, luaK_codeAsBc(fs, loopfor, basereg, NO_JUMP), blockinit);
-  luaK_fixfor(fs, prep, luaK_getlabel(fs));
   removelocalvars(ls, nvar, 1);
 }
 
@@ -986,13 +986,15 @@ static void fornum (LexState *ls, TString *varname) {
   new_localvar(ls, varname, 0);
   new_localvarstr(ls, "(limit)", 1);
   new_localvarstr(ls, "(step)", 2);
-  forbody(ls, 3, OP_FORPREP, OP_FORLOOP);
+  luaK_codeABC(fs, OP_SUB, fs->freereg - 3, fs->freereg - 3, fs->freereg - 1);
+  forbody(ls, 3, OP_FORLOOP);
 }
 
 
 static void forlist (LexState *ls, TString *indexname) {
   /* forlist -> NAME,NAME IN exp1 forbody */
   TString *valname;
+  FuncState *fs = ls->fs;
   check(ls, ',');
   valname = str_checkname(ls);
   next(ls);  /* skip var name */
@@ -1002,8 +1004,9 @@ static void forlist (LexState *ls, TString *indexname) {
   new_localvarstr(ls, "(index)", 1);
   new_localvar(ls, indexname, 2);
   new_localvar(ls, valname, 3);
-  luaK_reserveregs(ls->fs, 3);  /* registers for control, index and val */
-  forbody(ls, 4, OP_TFORPREP, OP_TFORLOOP);
+  luaK_reserveregs(fs, 3);  /* registers for control, index and val */
+  luaK_codeABc(fs, OP_LOADK, fs->freereg - 3, luaK_numberK(fs, -1));
+  forbody(ls, 4, OP_TFORLOOP);
 }
 
 

+ 32 - 35
lvm.c

@@ -26,6 +26,9 @@
 #include "lvm.h"
 
 
+/* limit for table tag-method chains (to avoid loops) */
+#define MAXTAGLOOP	10000
+
 
 static void luaV_checkGC (lua_State *L, StkId top) {
   if (G(L)->nblocks >= G(L)->GCthreshold) {
@@ -65,6 +68,8 @@ static void traceexec (lua_State *L, lua_Hook linehook) {
   int *lineinfo = ci_func(ci)->l.p->lineinfo;
   int pc = cast(int, *ci->pc - ci_func(ci)->l.p->code) - 1;
   int newline;
+  if (testOpMode(GET_OPCODE(*(*ci->pc - 1)), OpModeNoTrace))
+    return;
   if (ci->line == -1) return;  /* no linehooks for this function */
   else if (ci->line == 0) {  /* first linehook? */
     if (pc == 0) {  /* function is starting now? */
@@ -123,6 +128,7 @@ static void callTM (lua_State *L, const TObject *f,
 */
 void luaV_gettable (lua_State *L, StkId t, TObject *key, StkId res) {
   const TObject *tm;
+  int loop = 0;
   init:
   if (ttype(t) == LUA_TTABLE) {  /* `t' is a table? */
     Table *et = hvalue(t)->metatable;
@@ -145,6 +151,7 @@ void luaV_gettable (lua_State *L, StkId t, TObject *key, StkId res) {
   if (ttype(tm) == LUA_TFUNCTION)
     callTMres(L, tm, t, key, res);
   else {
+    if (++loop == MAXTAGLOOP) luaD_error(L, "loop in gettable");
     t = (StkId)tm;  /* ?? */
     goto init;  /* return luaV_gettable(L, tm, key, res); */
   }
@@ -156,6 +163,7 @@ void luaV_gettable (lua_State *L, StkId t, TObject *key, StkId res) {
 */
 void luaV_settable (lua_State *L, StkId t, TObject *key, StkId val) {
   const TObject *tm;
+  int loop = 0;
   init:
   if (ttype(t) == LUA_TTABLE) {  /* `t' is a table? */
     Table *et = hvalue(t)->metatable;
@@ -173,6 +181,7 @@ void luaV_settable (lua_State *L, StkId t, TObject *key, StkId val) {
   if (ttype(tm) == LUA_TFUNCTION)
     callTM(L, tm, t, key, val);
   else {
+    if (++loop == MAXTAGLOOP) luaD_error(L, "loop in settable");
     t = (StkId)tm;  /* ?? */
     goto init;  /* luaV_settable(L, tm, key, val); */
   }
@@ -301,8 +310,8 @@ static void powOp (lua_State *L, StkId ra, StkId rb, StkId rc) {
 #define Arith(op, optm)	{ \
   const TObject *b = RB(i); const TObject *c = RKC(i);		\
   TObject tempb, tempc; \
-  if ((ttype(b) == LUA_TNUMBER || (b = luaV_tonumber(b, &tempb)) != NULL) && \
-      (ttype(c) == LUA_TNUMBER || (c = luaV_tonumber(c, &tempc)) != NULL)) { \
+  if ((b = luaV_tonumber(b, &tempb)) != NULL && \
+      (c = luaV_tonumber(c, &tempc)) != NULL) { \
     setnvalue(ra, nvalue(b) op nvalue(c));		\
   } else		\
     call_arith(L, RB(i), RKC(i), ra, optm); \
@@ -423,7 +432,7 @@ StkId luaV_execute (lua_State *L) {
       }
       case OP_UNM: {
         const TObject *rb = RB(i);
-        if (ttype(rb) == LUA_TNUMBER || (rb=luaV_tonumber(rb, ra)) != NULL) {
+        if ((rb=luaV_tonumber(rb, ra)) != NULL) {
           setnvalue(ra, -nvalue(rb));
         }
         else {
@@ -441,7 +450,7 @@ StkId luaV_execute (lua_State *L) {
       case OP_CONCAT: {
         int b = GETARG_B(i);
         int c = GETARG_C(i);
-        luaV_strconc(L, c-b+1, c);  /* this call may change `base' (and `ra') */
+        luaV_strconc(L, c-b+1, c);  /* may change `base' (and `ra') */
         setobj(base+GETARG_A(i), base+b);
         luaV_checkGC(L, base+c+1);
         break;
@@ -532,53 +541,41 @@ StkId luaV_execute (lua_State *L) {
         }
         break;
       }
-      case OP_FORPREP: {
-        if (luaV_tonumber(ra, ra) == NULL)
+      case OP_FORLOOP: {
+        lua_Number step, index, limit;
+        int j = GETARG_sBc(i);
+        pc += j;  /* jump back before tests (for error messages) */
+        if (ttype(ra) != LUA_TNUMBER)
           luaD_error(L, "`for' initial value must be a number");
         if (luaV_tonumber(ra+1, ra+1) == NULL)
           luaD_error(L, "`for' limit must be a number");
         if (luaV_tonumber(ra+2, ra+2) == NULL)
           luaD_error(L, "`for' step must be a number");
-        /* decrement index (to be incremented) */
-        chgnvalue(ra, nvalue(ra) - nvalue(ra+2));
-        pc += -GETARG_sBc(i);  /* `jump' to loop end (delta is negated here) */
-        /* store in `ra+1' total number of repetitions */
-        chgnvalue(ra+1, (nvalue(ra+1)-nvalue(ra))/nvalue(ra+2));
-        /* go through */
-      }
-      case OP_FORLOOP: {
-        runtime_check(L, ttype(ra+1) == LUA_TNUMBER &&
-                         ttype(ra+2) == LUA_TNUMBER);
-        if (ttype(ra) != LUA_TNUMBER)
-          luaD_error(L, "`for' index must be a number");
-        chgnvalue(ra+1, nvalue(ra+1) - 1);  /* decrement counter */
-        if (nvalue(ra+1) >= 0) {
-          chgnvalue(ra, nvalue(ra) + nvalue(ra+2));  /* increment index */
-          dojump(pc, i);  /* repeat loop */
-        }
+        step = nvalue(ra+2);
+        index = nvalue(ra) + step;  /* increment index */
+        limit = nvalue(ra+1);
+        if (step > 0 ? index <= limit : index >= limit)
+          chgnvalue(ra, index);  /* update index */
+        else
+          pc -= j;  /* undo jump */
         break;
       }
-      case OP_TFORPREP: {
-        if (ttype(ra) != LUA_TTABLE)
-          luaD_error(L, "`for' table must be a table");
-        setnvalue(ra+1, -1);  /* initial index */
-        setnilvalue(ra+2);
-        setnilvalue(ra+3);
-        pc += -GETARG_sBc(i);  /* `jump' to loop end (delta is negated here) */
-        /* go through */
-      }
       case OP_TFORLOOP: {
         Table *t;
         int n;
-        runtime_check(L, ttype(ra) == LUA_TTABLE &&
-                         ttype(ra+1) == LUA_TNUMBER);
+        int j = GETARG_sBc(i);
+        pc += j;  /* jump back before tests (for error messages) */
+        if (ttype(ra) != LUA_TTABLE)
+          luaD_error(L, "`for' table must be a table");
+        runtime_check(L, ttype(ra+1) == LUA_TNUMBER);
         t = hvalue(ra);
         n = cast(int, nvalue(ra+1));
         n = luaH_nexti(t, n, ra+2);
         if (n != -1) {  /* repeat loop? */
           setnvalue(ra+1, n);  /* index */
-          dojump(pc, i);  /* repeat loop */
         }
+        else
+          pc -= j;  /* undo jump */
         break;
       }
       case OP_SETLIST: