From 38b0e6128da7796300e2e8621e87835e16539f5b Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 5 Feb 2002 20:39:12 -0200 Subject: [PATCH] simpler implementation for `for' loops --- lcode.c | 11 --------- lcode.h | 1 - ldebug.c | 40 ++++++++------------------------ lopcodes.c | 12 ++++------ lopcodes.h | 6 ++--- lparser.c | 15 +++++++----- lvm.c | 67 ++++++++++++++++++++++++++---------------------------- 7 files changed, 57 insertions(+), 95 deletions(-) diff --git a/lcode.c b/lcode.c index b8a4ed19..c50596fb 100644 --- a/lcode.c +++ b/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). diff --git a/lcode.h b/lcode.h index e9058393..0a6d7bf5 100644 --- a/lcode.h +++ b/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); diff --git a/ldebug.c b/ldebug.c index 9bdf90be..48090ae7 100644 --- a/ldebug.c +++ b/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)", diff --git a/lopcodes.c b/lopcodes.c index 4d43a3b1..7c452783 100644 --- a/lopcodes.c +++ b/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)< 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); } diff --git a/lvm.c b/lvm.c index a3ced1bc..94165d97 100644 --- a/lvm.c +++ b/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: