From 196c87c9cecfacf978f37de4ec69eba0a5971256 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Thu, 23 Nov 2017 14:41:16 -0200 Subject: [PATCH] no more 'stackless' implementation; 'luaV_execute' calls itself recursively to execute function calls. 'unroll' continues all executions suspended by an yield (through a long jump) --- ldebug.c | 3 ++- ldo.c | 66 +++++++++++++++---------------------------------------- ldo.h | 3 +-- llimits.h | 7 +++--- lparser.c | 12 ++++------ lstate.c | 28 +++++++++++++++++++++-- lstate.h | 17 +++++++------- ltests.h | 6 ++++- lvm.c | 60 +++++++++++++++++++------------------------------- 9 files changed, 90 insertions(+), 112 deletions(-) diff --git a/ldebug.c b/ldebug.c index 7323f23a..94b3596c 100644 --- a/ldebug.c +++ b/ldebug.c @@ -1,5 +1,5 @@ /* -** $Id: ldebug.c,v 2.143 2017/11/13 12:20:51 roberto Exp roberto $ +** $Id: ldebug.c,v 2.144 2017/11/13 15:36:52 roberto Exp roberto $ ** Debug Interface ** See Copyright Notice in lua.h */ @@ -724,6 +724,7 @@ l_noret luaG_errormsg (lua_State *L) { setobjs2s(L, L->top, L->top - 1); /* move argument */ setobjs2s(L, L->top - 1, errfunc); /* push function */ L->top++; /* assume EXTRA_STACK */ + luaE_incCcalls(L); luaD_callnoyield(L, L->top - 2, 1); /* call it */ } luaD_throw(L, LUA_ERRRUN); diff --git a/ldo.c b/ldo.c index 30954123..6b8162d2 100644 --- a/ldo.c +++ b/ldo.c @@ -1,5 +1,5 @@ /* -** $Id: ldo.c,v 2.172 2017/11/13 15:36:52 roberto Exp roberto $ +** $Id: ldo.c,v 2.173 2017/11/21 14:17:35 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -135,7 +135,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { - unsigned short oldnCcalls = L->nCcalls; + unsigned short oldnCcalls = L->nCcalls - L->nci; struct lua_longjmp lj; lj.status = LUA_OK; lj.previous = L->errorJmp; /* chain new error handler */ @@ -144,7 +144,7 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { (*f)(L, ud); ); L->errorJmp = lj.previous; /* restore old error handler */ - L->nCcalls = oldnCcalls; + L->nCcalls = oldnCcalls + L->nci; return lj.status; } @@ -299,7 +299,7 @@ static void callhook (lua_State *L, CallInfo *ci, int istail) { /* ** Check whether __call metafield of 'func' is a function. If so, put -** it in stack below original 'func' so that 'luaD_precall' can call +** it in stack below original 'func' so that 'luaD_call' can call ** it. Raise an error if __call metafield is not a function. */ StkId luaD_tryfuncTM (lua_State *L, StkId func) { @@ -417,13 +417,12 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n) { /* -** Prepares a function call: checks the stack, creates a new CallInfo -** entry, fills in the relevant information, calls hook if needed. -** If function is a C function, does the call, too. (Otherwise, leave -** the execution ('luaV_execute') to the caller, to allow stackless -** calls.) Returns true iff function has been executed (C function). +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. */ -int luaD_precall (lua_State *L, StkId func, int nresults) { +void luaD_call (lua_State *L, StkId func, int nresults) { lua_CFunction f; TValue *funcv = s2v(func); CallInfo *ci; @@ -449,7 +448,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, L->top - n, n); - return 1; + break; } case LUA_TLCL: { /* Lua function: prepare its call */ Proto *p = clLvalue(funcv)->p; @@ -469,46 +468,18 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { ci->callstatus = CIST_LUA; if (L->hookmask) callhook(L, ci, 0); - return 0; + luaV_execute(L); /* run the function */ + break; } default: { /* not a function */ func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ - return luaD_precall(L, func, nresults); /* now it must be a function */ + luaD_call(L, func, nresults); /* now it must be a function */ + break; } } } -/* -** Check appropriate error for stack overflow ("regular" overflow or -** overflow while handling stack overflow). If 'nCalls' is larger than -** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but -** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to -** allow overflow handling to work) -*/ -static void stackerror (lua_State *L) { - if (L->nCcalls == LUAI_MAXCCALLS) - luaG_runerror(L, "C stack overflow"); - else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) - luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ -} - - -/* -** Call a function (C or Lua). The function to be called is at *func. -** The arguments are on the stack, right after the function. -** When returns, all the results are on the stack, starting at the original -** function position. -*/ -void luaD_call (lua_State *L, StkId func, int nResults) { - if (++L->nCcalls >= LUAI_MAXCCALLS) - stackerror(L); - if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ - luaV_execute(L); /* call it */ - L->nCcalls--; -} - - /* ** Similar to 'luaD_call', but does not allow yields during the call */ @@ -541,7 +512,7 @@ static void finishCcall (lua_State *L, int status) { n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation function */ lua_lock(L); api_checknelems(L, n); - luaD_poscall(L, ci, L->top - n, n); /* finish 'luaD_precall' */ + luaD_poscall(L, ci, L->top - n, n); /* finish 'luaD_call' */ } @@ -629,8 +600,7 @@ static void resume (lua_State *L, void *ud) { StkId firstArg = L->top - n; /* first argument */ CallInfo *ci = L->ci; if (L->status == LUA_OK) { /* starting a coroutine? */ - if (!luaD_precall(L, firstArg - 1, LUA_MULTRET)) /* Lua function? */ - luaV_execute(L); /* call it */ + luaD_call(L, firstArg - 1, LUA_MULTRET); } else { /* resuming from previous yield */ lua_assert(L->status == LUA_YIELD); @@ -645,7 +615,7 @@ static void resume (lua_State *L, void *ud) { api_checknelems(L, n); firstArg = L->top - n; /* yield results come from continuation */ } - luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_precall' */ + luaD_poscall(L, ci, firstArg, n); /* finish 'luaD_call' */ } unroll(L, NULL); /* run continuation */ } @@ -688,7 +658,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, : L->top - (L->ci->func + 1); L->nny = oldnny; /* restore 'nny' */ L->nCcalls--; - lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0)); + // lua_assert(L->nCcalls == ((from) ? from->nCcalls : 0)); lua_unlock(L); return status; } diff --git a/ldo.h b/ldo.h index 2640c088..4fba45b5 100644 --- a/ldo.h +++ b/ldo.h @@ -1,5 +1,5 @@ /* -** $Id: ldo.h,v 2.33 2017/11/07 13:25:26 roberto Exp roberto $ +** $Id: ldo.h,v 2.34 2017/11/21 14:18:03 roberto Exp roberto $ ** Stack and Call structure of Lua ** See Copyright Notice in lua.h */ @@ -47,7 +47,6 @@ typedef void (*Pfunc) (lua_State *L, void *ud); LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, const char *mode); LUAI_FUNC void luaD_hook (lua_State *L, int event, int line); -LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); diff --git a/llimits.h b/llimits.h index 4b35dfcb..4e641008 100644 --- a/llimits.h +++ b/llimits.h @@ -1,5 +1,5 @@ /* -** $Id: llimits.h,v 1.143 2017/06/01 19:16:34 roberto Exp roberto $ +** $Id: llimits.h,v 1.144 2017/06/27 11:35:01 roberto Exp roberto $ ** Limits, basic types, and some other 'installation-dependent' definitions ** See Copyright Notice in lua.h */ @@ -140,10 +140,11 @@ typedef LUAI_UACINT l_uacInt; /* ** maximum depth for nested C calls and syntactical nested non-terminals -** in a program. (Value must fit in an unsigned short int.) +** in a program. (Value must fit in an unsigned short int. It must also +** be compatible with the size of the C stack.) */ #if !defined(LUAI_MAXCCALLS) -#define LUAI_MAXCCALLS 200 +#define LUAI_MAXCCALLS 1000 #endif diff --git a/lparser.c b/lparser.c index 0637a0b7..3eb83fba 100644 --- a/lparser.c +++ b/lparser.c @@ -1,5 +1,5 @@ /* -** $Id: lparser.c,v 2.166 2017/09/28 16:53:29 roberto Exp roberto $ +** $Id: lparser.c,v 2.167 2017/10/04 21:53:03 roberto Exp roberto $ ** Lua Parser ** See Copyright Notice in lua.h */ @@ -330,11 +330,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { } -static void enterlevel (LexState *ls) { - lua_State *L = ls->L; - ++L->nCcalls; - checklimit(ls->fs, L->nCcalls, LUAI_MAXCCALLS, "C levels"); -} +#define enterlevel(ls) luaE_incCcalls((ls)->L) #define leavelevel(ls) ((ls)->L->nCcalls--) @@ -1188,9 +1184,9 @@ static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { suffixedexp(ls, &nv.v); if (!vkisindexed(nv.v.k)) check_conflict(ls, lh, &nv.v); - checklimit(ls->fs, nvars + ls->L->nCcalls, LUAI_MAXCCALLS, - "C levels"); + luaE_incCcalls(ls->L); /* control recursion depth */ assignment(ls, &nv, nvars+1); + ls->L->nCcalls--; } else { /* assignment -> '=' explist */ int nexps; diff --git a/lstate.c b/lstate.c index 54e390b5..22702a00 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.146 2017/11/07 13:25:26 roberto Exp roberto $ +** $Id: lstate.c,v 2.147 2017/11/13 15:36:52 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -97,8 +97,28 @@ void luaE_setdebt (global_State *g, l_mem debt) { } +/* +** Increment count of "C calls" and check for overflows. In case of +** a stack overflow, check appropriate error ("regular" overflow or +** overflow while handling stack overflow). If 'nCalls' is larger than +** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but +** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to +** allow overflow handling to work) +*/ +void luaE_incCcalls (lua_State *L) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } +} + + CallInfo *luaE_extendCI (lua_State *L) { - CallInfo *ci = luaM_new(L, CallInfo); + CallInfo *ci; + luaE_incCcalls(L); + ci = luaM_new(L, CallInfo); lua_assert(L->ci->next == NULL); L->ci->next = ci; ci->previous = L->ci; @@ -116,11 +136,13 @@ void luaE_freeCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next = ci->next; ci->next = NULL; + L->nCcalls -= L->nci; /* to subtract removed elements from 'nCcalls' */ while ((ci = next) != NULL) { next = ci->next; luaM_free(L, ci); L->nci--; } + L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */ } @@ -130,6 +152,7 @@ void luaE_freeCI (lua_State *L) { void luaE_shrinkCI (lua_State *L) { CallInfo *ci = L->ci; CallInfo *next2; /* next's next */ + L->nCcalls -= L->nci; /* to subtract removed elements from 'nCcalls' */ /* while there are two nexts */ while (ci->next != NULL && (next2 = ci->next->next) != NULL) { luaM_free(L, ci->next); /* free next */ @@ -138,6 +161,7 @@ void luaE_shrinkCI (lua_State *L) { next2->previous = ci; ci = next2; /* keep next's next */ } + L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */ } diff --git a/lstate.h b/lstate.h index 02715dfb..fad54634 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.150 2017/11/07 13:25:26 roberto Exp roberto $ +** $Id: lstate.h,v 2.151 2017/11/13 15:36:52 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -104,7 +104,7 @@ typedef struct CallInfo { int nyield; /* number of values yielded */ } u2; short nresults; /* expected number of results from this function */ - unsigned short callstatus; + lu_byte callstatus; } CallInfo; @@ -114,13 +114,11 @@ typedef struct CallInfo { #define CIST_OAH (1<<0) /* original value of 'allowhook' */ #define CIST_LUA (1<<1) /* call is running a Lua function */ #define CIST_HOOKED (1<<2) /* call is running a debug hook */ -#define CIST_FRESH (1<<3) /* call is running on a fresh invocation - of luaV_execute */ -#define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ -#define CIST_TAIL (1<<5) /* call was tail called */ -#define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ -#define CIST_LEQ (1<<7) /* using __lt for __le */ -#define CIST_FIN (1<<8) /* call is running a finalizer */ +#define CIST_YPCALL (1<<3) /* call is a yieldable protected call */ +#define CIST_TAIL (1<<4) /* call was tail called */ +#define CIST_HOOKYIELD (1<<5) /* last hook called yielded */ +#define CIST_LEQ (1<<6) /* using __lt for __le */ +#define CIST_FIN (1<<7) /* call is running a finalizer */ #define isLua(ci) ((ci)->callstatus & CIST_LUA) @@ -256,6 +254,7 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); LUAI_FUNC void luaE_freeCI (lua_State *L); LUAI_FUNC void luaE_shrinkCI (lua_State *L); +LUAI_FUNC void luaE_incCcalls (lua_State *L); #endif diff --git a/ltests.h b/ltests.h index e3d1ca1e..04aa91b3 100644 --- a/ltests.h +++ b/ltests.h @@ -1,5 +1,5 @@ /* -** $Id: ltests.h,v 2.51 2017/06/27 11:35:31 roberto Exp roberto $ +** $Id: ltests.h,v 2.52 2017/11/13 12:19:35 roberto Exp roberto $ ** Internal Header for Debugging of the Lua Implementation ** See Copyright Notice in lua.h */ @@ -34,6 +34,10 @@ #define lua_assert(c) assert(c) +/* compiled with -O0, Lua uses a lot of C stack space... */ +#undef LUAI_MAXCCALLS +#define LUAI_MAXCCALLS 300 + /* to avoid warnings, and to make sure value is really unused */ #define UNUSED(x) (x=0, (void)(x)) diff --git a/lvm.c b/lvm.c index 457da1dd..ca65c798 100644 --- a/lvm.c +++ b/lvm.c @@ -1,5 +1,9 @@ /* -** $Id: lvm.c,v 2.314 2017/11/22 18:41:20 roberto Exp roberto $ +<<<<<<< lvm.c +** $Id: lvm.c,v 2.313 2017/11/21 14:17:35 roberto Exp roberto $ +======= +** $Id: lvm.c,v 2.315 2017/11/22 19:15:44 roberto Exp $ +>>>>>>> 2.315 ** Lua virtual machine ** See Copyright Notice in lua.h */ @@ -829,20 +833,12 @@ void luaV_finishOp (lua_State *L) { void luaV_execute (lua_State *L) { - CallInfo *ci = L->ci; /* local copy of 'L->ci' */ - LClosure *cl; - TValue *k; - StkId base; /* local copy of 'ci->func + 1' */ - int trap; - const Instruction *pc; /* local copy of 'ci->u.l.savedpc' */ - ci->callstatus |= CIST_FRESH; /* fresh invocation of 'luaV_execute" */ - newframe: /* reentry point when frame changes (call/return) */ - lua_assert(ci == L->ci); - cl = clLvalue(s2v(ci->func)); /* local reference to function's closure */ - k = cl->p->k; /* local reference to function's constant table */ - updatetrap(ci); - updatebase(ci); - pc = ci->u.l.savedpc; + CallInfo *ci = L->ci; + LClosure *cl = clLvalue(s2v(ci->func)); + TValue *k = cl->p->k; + StkId base = ci->func + 1; + int trap = ci->u.l.trap; + const Instruction *pc = ci->u.l.savedpc; /* main loop of interpreter */ for (;;) { Instruction i; @@ -1418,20 +1414,13 @@ void luaV_execute (lua_State *L) { vmcase(OP_CALL) { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; - int isC; if (b != 0) /* fixed number of arguments? */ L->top = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ - Protect(isC = luaD_precall(L, ra, nresults)); - if (isC) { /* C function? */ - if (nresults >= 0) /* fixed number of results? */ - L->top = ci->top; /* correct top */ - /* else leave top for next instruction */ - } - else { /* Lua function */ - ci = L->ci; - goto newframe; /* restart luaV_execute over new Lua function */ - } + Protect(luaD_call(L, ra, nresults)); + if (nresults >= 0) /* fixed number of results? */ + L->top = ci->top; /* correct top */ + /* else leave top for next instruction */ vmbreak; } vmcase(OP_TAILCALL) { @@ -1447,12 +1436,15 @@ void luaV_execute (lua_State *L) { b++; /* there is now one extra argument */ } if (!ttisLclosure(s2v(ra))) /* C function? */ - Protect(luaD_precall(L, ra, LUA_MULTRET)); /* call it */ + Protect(luaD_call(L, ra, LUA_MULTRET)); /* call it */ else { /* tail call */ if (cl->p->sizep > 0) /* close upvalues from previous call */ luaF_close(L, ci->func + 1); luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ - goto newframe; /* restart luaV_execute over new Lua function */ + cl = clLvalue(s2v(ci->func)); + k = cl->p->k; + updatebase(ci); + pc = ci->u.l.savedpc; } vmbreak; } @@ -1461,16 +1453,8 @@ void luaV_execute (lua_State *L) { if (cl->p->sizep > 0) luaF_close(L, base); savepc(L); - b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); - if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ - return; /* external invocation: return */ - else { /* invocation via reentry: continue execution */ - ci = L->ci; - if (b) L->top = ci->top; - lua_assert(isLua(ci)); - lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); - goto newframe; /* restart luaV_execute over new Lua function */ - } + luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); + return; /* external invocation: return */ } vmcase(OP_FORLOOP) { if (ttisinteger(s2v(ra))) { /* integer loop? */