From 6d973e44da4263d660d9165eb4acfa0a7710abd4 Mon Sep 17 00:00:00 2001 From: Paul Kulchenko Date: Fri, 23 Jul 2021 16:08:57 -0700 Subject: [PATCH] Upgrade Lua to 5.4.3. Based on https://github.com/lua/lua/releases/tag/v5.4.3 (commit eadd8c7). --- third_party/lua/lapi.c | 15 +++---- third_party/lua/lauxlib.c | 44 ++++++++++++-------- third_party/lua/lauxlib.h | 8 ++-- third_party/lua/lbaselib.c | 12 +++--- third_party/lua/lcode.c | 3 +- third_party/lua/lcorolib.c | 10 ++--- third_party/lua/ldblib.c | 6 +-- third_party/lua/ldebug.c | 23 +++++++---- third_party/lua/ldo.c | 52 +++++++++++++++--------- third_party/lua/ldo.h | 2 +- third_party/lua/lfunc.c | 40 +++++++++++++----- third_party/lua/lgc.c | 38 ++++++++++++------ third_party/lua/liolib.c | 17 ++++---- third_party/lua/llimits.h | 16 -------- third_party/lua/lmathlib.c | 5 ++- third_party/lua/lmem.c | 33 ++++++++------- third_party/lua/loadlib.c | 17 ++++---- third_party/lua/lobject.h | 5 ++- third_party/lua/lopcodes.h | 14 +++---- third_party/lua/loslib.c | 8 ++-- third_party/lua/lparser.c | 8 ++-- third_party/lua/lstate.c | 3 +- third_party/lua/lstate.h | 1 + third_party/lua/lstring.c | 8 ++-- third_party/lua/lstrlib.c | 41 ++++++++++--------- third_party/lua/ltable.c | 69 ++++++++++++++++++++------------ third_party/lua/ltablib.c | 9 +++-- third_party/lua/ltests.c | 2 + third_party/lua/ltm.c | 5 ++- third_party/lua/lua.h | 6 +-- third_party/lua/luaconf.h | 28 +++++++++++++ third_party/lua/lvm.c | 52 +++++++++++++----------- third_party/lua/lvm.h | 6 ++- third_party/lua/test/all.lua | 14 +------ third_party/lua/test/api.lua | 26 +++++++++++- third_party/lua/test/cstack.lua | 12 ++++-- third_party/lua/test/errors.lua | 15 +++++++ third_party/lua/test/gc.lua | 8 ++++ third_party/lua/test/locals.lua | 6 ++- third_party/lua/test/packtests | 1 + third_party/lua/test/tracegc.lua | 40 ++++++++++++++++++ 41 files changed, 464 insertions(+), 264 deletions(-) create mode 100644 third_party/lua/test/tracegc.lua diff --git a/third_party/lua/lapi.c b/third_party/lua/lapi.c index 2d066d9e3..ce9ba51d1 100644 --- a/third_party/lua/lapi.c +++ b/third_party/lua/lapi.c @@ -166,7 +166,7 @@ LUA_API int lua_gettop (lua_State *L) { LUA_API void lua_settop (lua_State *L, int idx) { CallInfo *ci; - StkId func; + StkId func, newtop; ptrdiff_t diff; /* difference for new top */ lua_lock(L); ci = L->ci; @@ -181,12 +181,13 @@ LUA_API void lua_settop (lua_State *L, int idx) { api_check(L, -(idx+1) <= (L->top - (func + 1)), "invalid new top"); diff = idx + 1; /* will "subtract" index (as it is negative) */ } -#if defined(LUA_COMPAT_5_4_0) - if (diff < 0 && hastocloseCfunc(ci->nresults)) - luaF_close(L, L->top + diff, CLOSEKTOP, 0); -#endif - api_check(L, L->tbclist < L->top + diff, "cannot pop an unclosed slot"); - L->top += diff; + api_check(L, L->tbclist < L->top, "previous pop of an unclosed slot"); + newtop = L->top + diff; + if (diff < 0 && L->tbclist >= newtop) { + lua_assert(hastocloseCfunc(ci->nresults)); + luaF_close(L, newtop, CLOSEKTOP, 0); + } + L->top = newtop; /* correct top only after closing any upvalue */ lua_unlock(L); } diff --git a/third_party/lua/lauxlib.c b/third_party/lua/lauxlib.c index 61fe1fe6c..0c3a2bb97 100644 --- a/third_party/lua/lauxlib.c +++ b/third_party/lua/lauxlib.c @@ -178,7 +178,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int arg, const char *extramsg) { } -int luaL_typeerror (lua_State *L, int arg, const char *tname) { +LUALIB_API int luaL_typeerror (lua_State *L, int arg, const char *tname) { const char *msg; const char *typearg; /* name for the type of the actual argument */ if (luaL_getmetafield(L, arg, "__name") == LUA_TSTRING) @@ -364,7 +364,7 @@ LUALIB_API int luaL_checkoption (lua_State *L, int arg, const char *def, ** but without 'msg'.) */ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { - if (!lua_checkstack(L, space)) { + if (l_unlikely(!lua_checkstack(L, space))) { if (msg) luaL_error(L, "stack overflow (%s)", msg); else @@ -374,20 +374,20 @@ LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *msg) { LUALIB_API void luaL_checktype (lua_State *L, int arg, int t) { - if (lua_type(L, arg) != t) + if (l_unlikely(lua_type(L, arg) != t)) tag_error(L, arg, t); } LUALIB_API void luaL_checkany (lua_State *L, int arg) { - if (lua_type(L, arg) == LUA_TNONE) + if (l_unlikely(lua_type(L, arg) == LUA_TNONE)) luaL_argerror(L, arg, "value expected"); } LUALIB_API const char *luaL_checklstring (lua_State *L, int arg, size_t *len) { const char *s = lua_tolstring(L, arg, len); - if (!s) tag_error(L, arg, LUA_TSTRING); + if (l_unlikely(!s)) tag_error(L, arg, LUA_TSTRING); return s; } @@ -406,7 +406,7 @@ LUALIB_API const char *luaL_optlstring (lua_State *L, int arg, LUALIB_API lua_Number luaL_checknumber (lua_State *L, int arg) { int isnum; lua_Number d = lua_tonumberx(L, arg, &isnum); - if (!isnum) + if (l_unlikely(!isnum)) tag_error(L, arg, LUA_TNUMBER); return d; } @@ -428,7 +428,7 @@ static void interror (lua_State *L, int arg) { LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int arg) { int isnum; lua_Integer d = lua_tointegerx(L, arg, &isnum); - if (!isnum) { + if (l_unlikely(!isnum)) { interror(L, arg); } return d; @@ -461,7 +461,7 @@ static void *resizebox (lua_State *L, int idx, size_t newsize) { lua_Alloc allocf = lua_getallocf(L, &ud); UBox *box = (UBox *)lua_touserdata(L, idx); void *temp = allocf(ud, box->box, box->bsize, newsize); - if (temp == NULL && newsize > 0) { /* allocation error? */ + if (l_unlikely(temp == NULL && newsize > 0)) { /* allocation error? */ lua_pushliteral(L, "not enough memory"); lua_error(L); /* raise a memory error */ } @@ -501,13 +501,22 @@ static void newbox (lua_State *L) { #define buffonstack(B) ((B)->b != (B)->init.b) +/* +** Whenever buffer is accessed, slot 'idx' must either be a box (which +** cannot be NULL) or it is a placeholder for the buffer. +*/ +#define checkbufferlevel(B,idx) \ + lua_assert(buffonstack(B) ? lua_touserdata(B->L, idx) != NULL \ + : lua_touserdata(B->L, idx) == (void*)B) + + /* ** Compute new size for buffer 'B', enough to accommodate extra 'sz' ** bytes. */ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { size_t newsize = B->size * 2; /* double buffer size */ - if (MAX_SIZET - sz < B->n) /* overflow in (B->n + sz)? */ + if (l_unlikely(MAX_SIZET - sz < B->n)) /* overflow in (B->n + sz)? */ return luaL_error(B->L, "buffer too large"); if (newsize < B->n + sz) /* double is not big enough? */ newsize = B->n + sz; @@ -517,10 +526,11 @@ static size_t newbuffsize (luaL_Buffer *B, size_t sz) { /* ** Returns a pointer to a free area with at least 'sz' bytes in buffer -** 'B'. 'boxidx' is the relative position in the stack where the -** buffer's box is or should be. +** 'B'. 'boxidx' is the relative position in the stack where is the +** buffer's box or its placeholder. */ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { + checkbufferlevel(B, boxidx); if (B->size - B->n >= sz) /* enough space? */ return B->b + B->n; else { @@ -531,6 +541,7 @@ static char *prepbuffsize (luaL_Buffer *B, size_t sz, int boxidx) { if (buffonstack(B)) /* buffer already has a box? */ newbuff = (char *)resizebox(L, boxidx, newsize); /* resize it */ else { /* no box yet */ + lua_remove(L, boxidx); /* remove placeholder */ newbox(L); /* create a new box */ lua_insert(L, boxidx); /* move box to its intended position */ lua_toclose(L, boxidx); @@ -567,11 +578,11 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { LUALIB_API void luaL_pushresult (luaL_Buffer *B) { lua_State *L = B->L; + checkbufferlevel(B, -1); lua_pushlstring(L, B->b, B->n); - if (buffonstack(B)) { + if (buffonstack(B)) lua_closeslot(L, -2); /* close the box */ - lua_remove(L, -2); /* remove box from the stack */ - } + lua_remove(L, -2); /* remove box or placeholder from the stack */ } @@ -606,6 +617,7 @@ LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { B->b = B->init.b; B->n = 0; B->size = LUAL_BUFFERSIZE; + lua_pushlightuserdata(L, (void*)B); /* push placeholder */ } @@ -847,7 +859,7 @@ LUALIB_API lua_Integer luaL_len (lua_State *L, int idx) { int isnum; lua_len(L, idx); l = lua_tointegerx(L, -1, &isnum); - if (!isnum) + if (l_unlikely(!isnum)) luaL_error(L, "object length is not an integer"); lua_pop(L, 1); /* remove object */ return l; @@ -1060,7 +1072,7 @@ static void warnfon (void *ud, const char *message, int tocont) { LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); - if (L) { + if (l_likely(L)) { lua_atpanic(L, &panic); lua_setwarnf(L, warnfoff, L); /* default is warnings off */ } diff --git a/third_party/lua/lauxlib.h b/third_party/lua/lauxlib.h index 4bcd63dc4..81e3d47b9 100644 --- a/third_party/lua/lauxlib.h +++ b/third_party/lua/lauxlib.h @@ -9,11 +9,9 @@ #define lauxlib_h #include "libc/stdio/stdio.h" +#include "third_party/lua/luaconf.h" #include "third_party/lua/lua.h" -/* clang-format off */ - - /* global table */ #define LUA_GNAME "_G" @@ -129,10 +127,10 @@ LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) #define luaL_argcheck(L, cond,arg,extramsg) \ - ((void)((cond) || luaL_argerror(L, (arg), (extramsg)))) + ((void)(luai_likely(cond) || luaL_argerror(L, (arg), (extramsg)))) #define luaL_argexpected(L,cond,arg,tname) \ - ((void)((cond) || luaL_typeerror(L, (arg), (tname)))) + ((void)(luai_likely(cond) || luaL_typeerror(L, (arg), (tname)))) #define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) #define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) diff --git a/third_party/lua/lbaselib.c b/third_party/lua/lbaselib.c index fe58794eb..25f37e902 100644 --- a/third_party/lua/lbaselib.c +++ b/third_party/lua/lbaselib.c @@ -132,7 +132,7 @@ static int luaB_setmetatable (lua_State *L) { int t = lua_type(L, 2); luaL_checktype(L, 1, LUA_TTABLE); luaL_argexpected(L, t == LUA_TNIL || t == LUA_TTABLE, 2, "nil or table"); - if (luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL) + if (l_unlikely(luaL_getmetafield(L, 1, "__metatable") != LUA_TNIL)) return luaL_error(L, "cannot change a protected metatable"); lua_settop(L, 2); lua_setmetatable(L, 1); @@ -294,7 +294,7 @@ static int luaB_ipairs (lua_State *L) { static int load_aux (lua_State *L, int status, int envidx) { - if (status == LUA_OK) { + if (l_likely(status == LUA_OK)) { if (envidx != 0) { /* 'env' parameter? */ lua_pushvalue(L, envidx); /* environment for loaded function */ if (!lua_setupvalue(L, -2, 1)) /* set it as 1st upvalue */ @@ -350,7 +350,7 @@ static const char *generic_reader (lua_State *L, void *ud, size_t *size) { *size = 0; return NULL; } - else if (!lua_isstring(L, -1)) + else if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "reader function must return a string"); lua_replace(L, RESERVEDSLOT); /* save string in reserved slot */ return lua_tolstring(L, RESERVEDSLOT, size); @@ -388,7 +388,7 @@ static int dofilecont (lua_State *L, int d1, lua_KContext d2) { static int luaB_dofile (lua_State *L) { const char *fname = luaL_optstring(L, 1, NULL); lua_settop(L, 1); - if (luaL_loadfile(L, fname) != LUA_OK) + if (l_unlikely(luaL_loadfile(L, fname) != LUA_OK)) return lua_error(L); lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); return dofilecont(L, 0, 0); @@ -396,7 +396,7 @@ static int luaB_dofile (lua_State *L) { static int luaB_assert (lua_State *L) { - if (lua_toboolean(L, 1)) /* condition is true? */ + if (l_likely(lua_toboolean(L, 1))) /* condition is true? */ return lua_gettop(L); /* return all arguments */ else { /* error */ luaL_checkany(L, 1); /* there must be a condition */ @@ -432,7 +432,7 @@ static int luaB_select (lua_State *L) { ** ignored). */ static int finishpcall (lua_State *L, int status, lua_KContext extra) { - if (status != LUA_OK && status != LUA_YIELD) { /* error? */ + if (l_unlikely(status != LUA_OK && status != LUA_YIELD)) { /* error? */ lua_pushboolean(L, 0); /* first result (false) */ lua_pushvalue(L, -2); /* error message */ return 2; /* return false, msg */ diff --git a/third_party/lua/lcode.c b/third_party/lua/lcode.c index 9f60523f8..b448fd185 100644 --- a/third_party/lua/lcode.c +++ b/third_party/lua/lcode.c @@ -1293,7 +1293,8 @@ static int validop (int op, TValue *v1, TValue *v2) { case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR: case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: { /* conversion errors */ lua_Integer i; - return (tointegerns(v1, &i) && tointegerns(v2, &i)); + return (luaV_tointegerns(v1, &i, LUA_FLOORN2I) && + luaV_tointegerns(v2, &i, LUA_FLOORN2I)); } case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD: /* division by 0 */ return (nvalue(v2) != 0); diff --git a/third_party/lua/lcorolib.c b/third_party/lua/lcorolib.c index 69c734bae..9c34df9ba 100644 --- a/third_party/lua/lcorolib.c +++ b/third_party/lua/lcorolib.c @@ -27,14 +27,14 @@ static lua_State *getco (lua_State *L) { */ static int auxresume (lua_State *L, lua_State *co, int narg) { int status, nres; - if (!lua_checkstack(co, narg)) { + if (l_unlikely(!lua_checkstack(co, narg))) { lua_pushliteral(L, "too many arguments to resume"); return -1; /* error flag */ } lua_xmove(L, co, narg); status = lua_resume(co, L, narg, &nres); - if (status == LUA_OK || status == LUA_YIELD) { - if (!lua_checkstack(L, nres + 1)) { + if (l_likely(status == LUA_OK || status == LUA_YIELD)) { + if (l_unlikely(!lua_checkstack(L, nres + 1))) { lua_pop(co, nres); /* remove results anyway */ lua_pushliteral(L, "too many results to resume"); return -1; /* error flag */ @@ -53,7 +53,7 @@ static int luaB_coresume (lua_State *L) { lua_State *co = getco(L); int r; r = auxresume(L, co, lua_gettop(L) - 1); - if (r < 0) { + if (l_unlikely(r < 0)) { lua_pushboolean(L, 0); lua_insert(L, -2); return 2; /* return false + error message */ @@ -69,7 +69,7 @@ static int luaB_coresume (lua_State *L) { static int luaB_auxwrap (lua_State *L) { lua_State *co = lua_tothread(L, lua_upvalueindex(1)); int r = auxresume(L, co, lua_gettop(L)); - if (r < 0) { /* error? */ + if (l_unlikely(r < 0)) { /* error? */ int stat = lua_status(co); if (stat != LUA_OK && stat != LUA_YIELD) { /* error in the coroutine? */ stat = lua_resetthread(co); /* close its tbc variables */ diff --git a/third_party/lua/ldblib.c b/third_party/lua/ldblib.c index aab4cdc70..ed9c48d31 100644 --- a/third_party/lua/ldblib.c +++ b/third_party/lua/ldblib.c @@ -30,7 +30,7 @@ static const char *const HOOKKEY = "_HOOKKEY"; ** checked. */ static void checkstack (lua_State *L, lua_State *L1, int n) { - if (L != L1 && !lua_checkstack(L1, n)) + if (l_unlikely(L != L1 && !lua_checkstack(L1, n))) luaL_error(L, "stack overflow"); } @@ -210,7 +210,7 @@ static int db_getlocal (lua_State *L) { lua_Debug ar; const char *name; int level = (int)luaL_checkinteger(L, arg + 1); - if (!lua_getstack(L1, level, &ar)) /* out of range? */ + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); checkstack(L, L1, 1); name = lua_getlocal(L1, &ar, nvar); @@ -235,7 +235,7 @@ static int db_setlocal (lua_State *L) { lua_Debug ar; int level = (int)luaL_checkinteger(L, arg + 1); int nvar = (int)luaL_checkinteger(L, arg + 2); - if (!lua_getstack(L1, level, &ar)) /* out of range? */ + if (l_unlikely(!lua_getstack(L1, level, &ar))) /* out of range? */ return luaL_argerror(L, arg+1, "level out of range"); luaL_checkany(L, arg+3); lua_settop(L, arg+3); diff --git a/third_party/lua/ldebug.c b/third_party/lua/ldebug.c index 2230ffd62..1f2c0785c 100644 --- a/third_party/lua/ldebug.c +++ b/third_party/lua/ldebug.c @@ -47,6 +47,8 @@ static int currentpc (CallInfo *ci) { ** an integer division gets the right place. When the source file has ** large sequences of empty/comment lines, it may need extra entries, ** so the original estimate needs a correction. +** If the original estimate is -1, the initial 'if' ensures that the +** 'while' will run at least once. ** The assertion that the estimate is a lower bound for the correct base ** is valid as long as the debug info has been generated with the same ** value for MAXIWTHABS or smaller. (Previous releases use a little @@ -60,7 +62,8 @@ static int getbaseline (const Proto *f, int pc, int *basepc) { else { int i = cast_uint(pc) / MAXIWTHABS - 1; /* get an estimate */ /* estimate must be a lower bond of the correct base */ - lua_assert(i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc); + lua_assert(i < 0 || + (i < f->sizeabslineinfo && f->abslineinfo[i].pc <= pc)); while (i + 1 < f->sizeabslineinfo && pc >= f->abslineinfo[i + 1].pc) i++; /* low estimate; adjust it */ *basepc = f->abslineinfo[i].pc; @@ -635,14 +638,18 @@ static const char *funcnamefromcode (lua_State *L, CallInfo *ci, /* -** The subtraction of two potentially unrelated pointers is -** not ISO C, but it should not crash a program; the subsequent -** checks are ISO C and ensure a correct result. +** Check whether pointer 'o' points to some value in the stack +** frame of the current function. Because 'o' may not point to a +** value in this stack, we cannot compare it with the region +** boundaries (undefined behaviour in ISO C). */ static int isinstack (CallInfo *ci, const TValue *o) { - StkId base = ci->func + 1; - ptrdiff_t i = cast(StkId, o) - base; - return (0 <= i && i < (ci->top - base) && s2v(base + i) == o); + StkId pos; + for (pos = ci->func + 1; pos < ci->top; pos++) { + if (o == s2v(pos)) + return 1; + } + return 0; /* not found */ } @@ -723,7 +730,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1, */ l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) { lua_Integer temp; - if (!tointegerns(p1, &temp)) + if (!luaV_tointegerns(p1, &temp, LUA_FLOORN2I)) p2 = p1; luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2)); } diff --git a/third_party/lua/ldo.c b/third_party/lua/ldo.c index 4ef41d32a..0ebffb139 100644 --- a/third_party/lua/ldo.c +++ b/third_party/lua/ldo.c @@ -155,8 +155,6 @@ int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { CallInfo *ci; UpVal *up; - if (oldstack == newstack) - return; /* stack address did not change */ L->top = (L->top - oldstack) + newstack; L->tbclist = (L->tbclist - oldstack) + newstack; for (up = L->openupval; up != NULL; up = up->u.open.next) @@ -174,19 +172,35 @@ static void correctstack (lua_State *L, StkId oldstack, StkId newstack) { #define ERRORSTACKSIZE (LUAI_MAXSTACK + 200) +/* +** Reallocate the stack to a new size, correcting all pointers into +** it. (There are pointers to a stack from its upvalues, from its list +** of call infos, plus a few individual pointers.) The reallocation is +** done in two steps (allocation + free) because the correction must be +** done while both addresses (the old stack and the new one) are valid. +** (In ISO C, any pointer use after the pointer has been deallocated is +** undefined behavior.) +** In case of allocation error, raise an error or return false according +** to 'raiseerror'. +*/ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { - int lim = stacksize(L); - StkId newstack = luaM_reallocvector(L, L->stack, - lim + EXTRA_STACK, newsize + EXTRA_STACK, StackValue); + int oldsize = stacksize(L); + int i; + StkId newstack = luaM_reallocvector(L, NULL, 0, + newsize + EXTRA_STACK, StackValue); lua_assert(newsize <= LUAI_MAXSTACK || newsize == ERRORSTACKSIZE); - if (unlikely(newstack == NULL)) { /* reallocation failed? */ + if (l_unlikely(newstack == NULL)) { /* reallocation failed? */ if (raiseerror) luaM_error(L); else return 0; /* do not raise an error */ } - for (; lim < newsize; lim++) - setnilvalue(s2v(newstack + lim + EXTRA_STACK)); /* erase new segment */ + /* number of elements to be copied to the new stack */ + i = ((oldsize <= newsize) ? oldsize : newsize) + EXTRA_STACK; + memcpy(newstack, L->stack, i * sizeof(StackValue)); + for (; i < newsize + EXTRA_STACK; i++) + setnilvalue(s2v(newstack + i)); /* erase new segment */ correctstack(L, L->stack, newstack); + luaM_freearray(L, L->stack, oldsize + EXTRA_STACK); L->stack = newstack; L->stack_last = L->stack + newsize; return 1; @@ -199,7 +213,7 @@ int luaD_reallocstack (lua_State *L, int newsize, int raiseerror) { */ int luaD_growstack (lua_State *L, int n, int raiseerror) { int size = stacksize(L); - if (unlikely(size > LUAI_MAXSTACK)) { + if (l_unlikely(size > LUAI_MAXSTACK)) { /* if stack is larger than maximum, thread is already using the extra space reserved for errors, that is, thread is handling a stack error; cannot grow further than that. */ @@ -215,7 +229,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) { newsize = LUAI_MAXSTACK; if (newsize < needed) /* but must respect what was asked for */ newsize = needed; - if (likely(newsize <= LUAI_MAXSTACK)) + if (l_likely(newsize <= LUAI_MAXSTACK)) return luaD_reallocstack(L, newsize, raiseerror); else { /* stack overflow */ /* add extra size to be able to handle the error message */ @@ -371,7 +385,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) { void luaD_tryfuncTM (lua_State *L, StkId func) { const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL); StkId p; - if (unlikely(ttisnil(tm))) + if (l_unlikely(ttisnil(tm))) luaG_callerror(L, s2v(func)); /* nothing to call */ for (p = L->top; p > func; p--) /* open space for metamethod */ setobjs2s(L, p, p-1); @@ -439,7 +453,7 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) { */ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { int wanted = ci->nresults; - if (L->hookmask && !hastocloseCfunc(wanted)) + if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) rethook(L, ci, nres); /* move results to proper place */ moveresults(L, ci->func, nres, wanted); @@ -505,7 +519,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { ci->top = L->top + LUA_MINSTACK; ci->func = func; lua_assert(ci->top <= L->stack_last); - if (L->hookmask & LUA_MASKCALL) { + if (l_unlikely(L->hookmask & LUA_MASKCALL)) { int narg = cast_int(L->top - func) - 1; luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); } @@ -551,7 +565,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { static void ccall (lua_State *L, StkId func, int nResults, int inc) { CallInfo *ci; L->nCcalls += inc; - if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) luaE_checkcstack(L); if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ @@ -595,7 +609,7 @@ void luaD_callnoyield (lua_State *L, StkId func, int nResults) { */ static int finishpcallk (lua_State *L, CallInfo *ci) { int status = getcistrecst(ci); /* get original status */ - if (status == LUA_OK) /* no error? */ + if (l_likely(status == LUA_OK)) /* no error? */ status = LUA_YIELD; /* was interrupted by an yield */ else { /* error */ StkId func = restorestack(L, ci->u2.funcidx); @@ -769,7 +783,7 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, status = luaD_rawrunprotected(L, resume, &nargs); /* continue running after recoverable errors */ status = precover(L, status); - if (likely(!errorstatus(status))) + if (l_likely(!errorstatus(status))) lua_assert(status == L->status); /* normal end or yield */ else { /* unrecoverable error */ L->status = cast_byte(status); /* mark thread as 'dead' */ @@ -795,7 +809,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, lua_lock(L); ci = L->ci; api_checknelems(L, nresults); - if (unlikely(!yieldable(L))) { + if (l_unlikely(!yieldable(L))) { if (L != G(L)->mainthread) luaG_runerror(L, "attempt to yield across a C-call boundary"); else @@ -848,7 +862,7 @@ int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) { struct CloseP pcl; pcl.level = restorestack(L, level); pcl.status = status; status = luaD_rawrunprotected(L, &closepaux, &pcl); - if (likely(status == LUA_OK)) /* no more errors? */ + if (l_likely(status == LUA_OK)) /* no more errors? */ return pcl.status; else { /* an error occurred; restore saved state and repeat */ L->ci = old_ci; @@ -871,7 +885,7 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; status = luaD_rawrunprotected(L, func, u); - if (unlikely(status != LUA_OK)) { /* an error occurred? */ + if (l_unlikely(status != LUA_OK)) { /* an error occurred? */ L->ci = old_ci; L->allowhook = old_allowhooks; status = luaD_closeprotected(L, old_top, status); diff --git a/third_party/lua/ldo.h b/third_party/lua/ldo.h index ac6ee556c..209f7b60f 100644 --- a/third_party/lua/ldo.h +++ b/third_party/lua/ldo.h @@ -23,7 +23,7 @@ ** at every check. */ #define luaD_checkstackaux(L,n,pre,pos) \ - if (L->stack_last - L->top <= (n)) \ + if (l_unlikely(L->stack_last - L->top <= (n))) \ { pre; luaD_growstack(L, n, 1); pos; } \ else { condmovestack(L,pre,pos); } diff --git a/third_party/lua/lfunc.c b/third_party/lua/lfunc.c index c0c4eda96..61ec4ccd4 100644 --- a/third_party/lua/lfunc.c +++ b/third_party/lua/lfunc.c @@ -149,6 +149,15 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) { } +/* +** Maximum value for deltas in 'tbclist', dependent on the type +** of delta. (This macro assumes that an 'L' is in scope where it +** is used.) +*/ +#define MAXDELTA \ + ((256ul << ((sizeof(L->stack->tbclist.delta) - 1) * 8)) - 1) + + /* ** Insert a variable in the list of to-be-closed variables. */ @@ -157,13 +166,11 @@ void luaF_newtbcupval (lua_State *L, StkId level) { if (l_isfalse(s2v(level))) return; /* false doesn't need to be closed */ checkclosemth(L, level); /* value must have a close method */ - while (level - L->tbclist > USHRT_MAX) { /* is delta too large? */ - L->tbclist += USHRT_MAX; /* create a dummy node at maximum delta */ - L->tbclist->tbclist.delta = USHRT_MAX; - L->tbclist->tbclist.isdummy = 1; + while (cast_uint(level - L->tbclist) > MAXDELTA) { + L->tbclist += MAXDELTA; /* create a dummy node at maximum delta */ + L->tbclist->tbclist.delta = 0; } - level->tbclist.delta = level - L->tbclist; - level->tbclist.isdummy = 0; + level->tbclist.delta = cast(unsigned short, level - L->tbclist); L->tbclist = level; } @@ -196,6 +203,19 @@ void luaF_closeupval (lua_State *L, StkId level) { } +/* +** Remove firt element from the tbclist plus its dummy nodes. +*/ +static void poptbclist (lua_State *L) { + StkId tbc = L->tbclist; + lua_assert(tbc->tbclist.delta > 0); /* first element cannot be dummy */ + tbc -= tbc->tbclist.delta; + while (tbc > L->stack && tbc->tbclist.delta == 0) + tbc -= MAXDELTA; /* remove dummy nodes */ + L->tbclist = tbc; +} + + /* ** Close all upvalues and to-be-closed variables up to the given stack ** level. @@ -205,11 +225,9 @@ void luaF_close (lua_State *L, StkId level, int status, int yy) { luaF_closeupval(L, level); /* first, close the upvalues */ while (L->tbclist >= level) { /* traverse tbc's down to that level */ StkId tbc = L->tbclist; /* get variable index */ - L->tbclist -= tbc->tbclist.delta; /* remove it from list */ - if (!tbc->tbclist.isdummy) { /* not a dummy entry? */ - prepcallclosemth(L, tbc, status, yy); /* close variable */ - level = restorestack(L, levelrel); - } + poptbclist(L); /* remove it from list */ + prepcallclosemth(L, tbc, status, yy); /* close variable */ + level = restorestack(L, levelrel); } } diff --git a/third_party/lua/lgc.c b/third_party/lua/lgc.c index afd5536f7..da7894b72 100644 --- a/third_party/lua/lgc.c +++ b/third_party/lua/lgc.c @@ -911,7 +911,7 @@ static void GCTM (lua_State *L) { L->ci->callstatus &= ~CIST_FIN; /* not running a finalizer anymore */ L->allowhook = oldah; /* restore hooks */ g->gcrunning = running; /* restore state */ - if (unlikely(status != LUA_OK)) { /* error while running __gc? */ + if (l_unlikely(status != LUA_OK)) { /* error while running __gc? */ luaE_warnerror(L, "__gc metamethod"); L->top--; /* pops error object */ } @@ -1570,52 +1570,64 @@ static int sweepstep (lua_State *L, global_State *g, static lu_mem singlestep (lua_State *L) { global_State *g = G(L); + lu_mem work; + lua_assert(!g->gcstopem); /* collector is not reentrant */ + g->gcstopem = 1; /* no emergency collections while collecting */ switch (g->gcstate) { case GCSpause: { restartcollection(g); g->gcstate = GCSpropagate; - return 1; + work = 1; + break; } case GCSpropagate: { if (g->gray == NULL) { /* no more gray objects? */ g->gcstate = GCSenteratomic; /* finish propagate phase */ - return 0; + work = 0; } else - return propagatemark(g); /* traverse one gray object */ + work = propagatemark(g); /* traverse one gray object */ + break; } case GCSenteratomic: { - lu_mem work = atomic(L); /* work is what was traversed by 'atomic' */ + work = atomic(L); /* work is what was traversed by 'atomic' */ entersweep(L); g->GCestimate = gettotalbytes(g); /* first estimate */; - return work; + break; } case GCSswpallgc: { /* sweep "regular" objects */ - return sweepstep(L, g, GCSswpfinobj, &g->finobj); + work = sweepstep(L, g, GCSswpfinobj, &g->finobj); + break; } case GCSswpfinobj: { /* sweep objects with finalizers */ - return sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + work = sweepstep(L, g, GCSswptobefnz, &g->tobefnz); + break; } case GCSswptobefnz: { /* sweep objects to be finalized */ - return sweepstep(L, g, GCSswpend, NULL); + work = sweepstep(L, g, GCSswpend, NULL); + break; } case GCSswpend: { /* finish sweeps */ checkSizes(L, g); g->gcstate = GCScallfin; - return 0; + work = 0; + break; } case GCScallfin: { /* call remaining finalizers */ if (g->tobefnz && !g->gcemergency) { - int n = runafewfinalizers(L, GCFINMAX); - return n * GCFINALIZECOST; + g->gcstopem = 0; /* ok collections during finalizers */ + work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; } else { /* emergency mode or no more finalizers */ g->gcstate = GCSpause; /* finish collection */ - return 0; + work = 0; } + break; } default: lua_assert(0); return 0; } + g->gcstopem = 0; + return work; } diff --git a/third_party/lua/liolib.c b/third_party/lua/liolib.c index d9beb99ce..8974e2823 100644 --- a/third_party/lua/liolib.c +++ b/third_party/lua/liolib.c @@ -178,7 +178,7 @@ static int f_tostring (lua_State *L) { static FILE *tofile (lua_State *L) { LStream *p = tolstream(L); - if (isclosed(p)) + if (l_unlikely(isclosed(p))) luaL_error(L, "attempt to use a closed file"); lua_assert(p->f); return p->f; @@ -253,7 +253,7 @@ static LStream *newfile (lua_State *L) { static void opencheck (lua_State *L, const char *fname, const char *mode) { LStream *p = newfile(L); p->f = fopen(fname, mode); - if (p->f == NULL) + if (l_unlikely(p->f == NULL)) luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno)); } @@ -301,7 +301,7 @@ static FILE *getiofile (lua_State *L, const char *findex) { LStream *p; lua_getfield(L, LUA_REGISTRYINDEX, findex); p = (LStream *)lua_touserdata(L, -1); - if (isclosed(p)) + if (l_unlikely(isclosed(p))) luaL_error(L, "default %s file is closed", findex + IOPREF_LEN); return p->f; } @@ -428,7 +428,7 @@ typedef struct { ** Add current char to buffer (if not out of space) and read next one */ static int nextc (RN *rn) { - if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */ + if (l_unlikely(rn->n >= L_MAXLENNUM)) { /* buffer overflow? */ rn->buff[0] = '\0'; /* invalidate result */ return 0; /* fail */ } @@ -491,8 +491,8 @@ static int read_number (lua_State *L, FILE *f) { ungetc(rn.c, rn.f); /* unread look-ahead char */ l_unlockfile(rn.f); rn.buff[rn.n] = '\0'; /* finish string */ - if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */ - return 1; /* ok */ + if (l_likely(lua_stringtonumber(L, rn.buff))) + return 1; /* ok, it is a valid number */ else { /* invalid format */ lua_pushnil(L); /* "result" to be removed */ return 0; /* read fails */ @@ -668,7 +668,8 @@ static int g_write (lua_State *L, FILE *f, int arg) { status = status && (fwrite(s, sizeof(char), l, f) == l); } } - if (status) return 1; /* file handle already on stack top */ + if (l_likely(status)) + return 1; /* file handle already on stack top */ else return luaL_fileresult(L, status, NULL); } @@ -695,7 +696,7 @@ static int f_seek (lua_State *L) { luaL_argcheck(L, (lua_Integer)offset == p3, 3, "not an integer in proper range"); op = l_fseek(f, offset, mode[op]); - if (op) + if (l_unlikely(op)) return luaL_fileresult(L, 0, NULL); /* error */ else { lua_pushinteger(L, (lua_Integer)l_ftell(f)); diff --git a/third_party/lua/llimits.h b/third_party/lua/llimits.h index 7e0071f50..e4a82be97 100644 --- a/third_party/lua/llimits.h +++ b/third_party/lua/llimits.h @@ -146,22 +146,6 @@ typedef LUAI_UACINT l_uacInt; #endif -/* -** macros to improve jump prediction (used mainly for error handling) -*/ -#if !defined(likely) - -#if defined(__GNUC__) -#define likely(x) (__builtin_expect(((x) != 0), 1)) -#define unlikely(x) (__builtin_expect(((x) != 0), 0)) -#else -#define likely(x) (x) -#define unlikely(x) (x) -#endif - -#endif - - /* ** non-return type */ diff --git a/third_party/lua/lmathlib.c b/third_party/lua/lmathlib.c index 24336840c..ac8d6070c 100644 --- a/third_party/lua/lmathlib.c +++ b/third_party/lua/lmathlib.c @@ -68,7 +68,7 @@ static int math_atan (lua_State *L) { static int math_toint (lua_State *L) { int valid; lua_Integer n = lua_tointegerx(L, 1, &valid); - if (valid) + if (l_likely(valid)) lua_pushinteger(L, n); else { luaL_checkany(L, 1); @@ -170,7 +170,8 @@ static int math_log (lua_State *L) { lua_Number base = luaL_checknumber(L, 2); #if !defined(LUA_USE_C89) if (base == l_mathop(2.0)) - res = l_mathop(log2)(x); else + res = l_mathop(log2)(x); + else #endif if (base == l_mathop(10.0)) res = l_mathop(log10)(x); diff --git a/third_party/lua/lmem.c b/third_party/lua/lmem.c index c50d10550..58a98fbe9 100644 --- a/third_party/lua/lmem.c +++ b/third_party/lua/lmem.c @@ -20,12 +20,12 @@ #if defined(EMERGENCYGCTESTS) /* -** First allocation will fail whenever not building initial state -** and not shrinking a block. (This fail will trigger 'tryagain' and -** a full GC cycle at every allocation.) +** First allocation will fail whenever not building initial state. +** (This fail will trigger 'tryagain' and a full GC cycle at every +** allocation.) */ static void *firsttry (global_State *g, void *block, size_t os, size_t ns) { - if (completestate(g) && ns > os) + if (completestate(g) && ns > 0) /* frees never fail */ return NULL; /* fail */ else /* normal allocation */ return (*g->frealloc)(g->ud, block, os, ns); @@ -79,7 +79,7 @@ void *luaM_growaux_ (lua_State *L, void *block, int nelems, int *psize, if (nelems + 1 <= size) /* does one extra element still fit? */ return block; /* nothing to be done */ if (size >= limit / 2) { /* cannot double it? */ - if (unlikely(size >= limit)) /* cannot grow even a little? */ + if (l_unlikely(size >= limit)) /* cannot grow even a little? */ luaG_runerror(L, "too many %s (limit is %d)", what, limit); size = limit; /* still have at least one free place */ } @@ -134,15 +134,17 @@ void luaM_free_ (lua_State *L, void *block, size_t osize) { /* -** In case of allocation fail, this function will call the GC to try -** to free some memory and then try the allocation again. -** (It should not be called when shrinking a block, because then the -** interpreter may be in the middle of a collection step.) +** In case of allocation fail, this function will do an emergency +** collection to free some memory and then try the allocation again. +** The GC should not be called while state is not fully built, as the +** collector is not yet fully initialized. Also, it should not be called +** when 'gcstopem' is true, because then the interpreter is in the +** middle of a collection step. */ static void *tryagain (lua_State *L, void *block, size_t osize, size_t nsize) { global_State *g = G(L); - if (completestate(g)) { /* is state fully build? */ + if (completestate(g) && !g->gcstopem) { luaC_fullgc(L, 1); /* try to free some memory... */ return (*g->frealloc)(g->ud, block, osize, nsize); /* try again */ } @@ -152,17 +154,14 @@ static void *tryagain (lua_State *L, void *block, /* ** Generic allocation routine. -** If allocation fails while shrinking a block, do not try again; the -** GC shrinks some blocks and it is not reentrant. */ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock; global_State *g = G(L); lua_assert((osize == 0) == (block == NULL)); newblock = firsttry(g, block, osize, nsize); - if (unlikely(newblock == NULL && nsize > 0)) { - if (nsize > osize) /* not shrinking a block? */ - newblock = tryagain(L, block, osize, nsize); + if (l_unlikely(newblock == NULL && nsize > 0)) { + newblock = tryagain(L, block, osize, nsize); if (newblock == NULL) /* still no memory? */ return NULL; /* do not update 'GCdebt' */ } @@ -175,7 +174,7 @@ void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *luaM_saferealloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { void *newblock = luaM_realloc_(L, block, osize, nsize); - if (unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ + if (l_unlikely(newblock == NULL && nsize > 0)) /* allocation failed? */ luaM_error(L); return newblock; } @@ -187,7 +186,7 @@ void *luaM_malloc_ (lua_State *L, size_t size, int tag) { else { global_State *g = G(L); void *newblock = firsttry(g, NULL, tag, size); - if (unlikely(newblock == NULL)) { + if (l_unlikely(newblock == NULL)) { newblock = tryagain(L, NULL, tag, size); if (newblock == NULL) luaM_error(L); diff --git a/third_party/lua/loadlib.c b/third_party/lua/loadlib.c index 3f33cdaef..0ad59525a 100644 --- a/third_party/lua/loadlib.c +++ b/third_party/lua/loadlib.c @@ -125,14 +125,16 @@ static void lsys_unloadlib (void *lib) { static void *lsys_load (lua_State *L, const char *path, int seeglb) { void *lib = dlopen(path, RTLD_NOW | (seeglb ? RTLD_GLOBAL : RTLD_LOCAL)); - if (lib == NULL) lua_pushstring(L, dlerror()); + if (l_unlikely(lib == NULL)) + lua_pushstring(L, dlerror()); return lib; } static lua_CFunction lsys_sym (lua_State *L, void *lib, const char *sym) { lua_CFunction f = cast_func(dlsym(lib, sym)); - if (f == NULL) lua_pushstring(L, dlerror()); + if (l_unlikely(f == NULL)) + lua_pushstring(L, dlerror()); return f; } @@ -402,7 +404,7 @@ static int ll_loadlib (lua_State *L) { const char *path = luaL_checkstring(L, 1); const char *init = luaL_checkstring(L, 2); int stat = lookforfunc(L, path, init); - if (stat == 0) /* no errors? */ + if (l_likely(stat == 0)) /* no errors? */ return 1; /* return the loaded function */ else { /* error; error message is on stack top */ luaL_pushfail(L); @@ -515,14 +517,14 @@ static const char *findfile (lua_State *L, const char *name, const char *path; lua_getfield(L, lua_upvalueindex(1), pname); path = lua_tostring(L, -1); - if (path == NULL) + if (l_unlikely(path == NULL)) luaL_error(L, "'package.%s' must be a string", pname); return searchpath(L, name, path, ".", dirsep); } static int checkload (lua_State *L, int stat, const char *filename) { - if (stat) { /* module loaded successfully? */ + if (l_likely(stat)) { /* module loaded successfully? */ lua_pushstring(L, filename); /* will be 2nd argument to module */ return 2; /* return open function and file name */ } @@ -615,13 +617,14 @@ static void findloader (lua_State *L, const char *name) { int i; luaL_Buffer msg; /* to build error message */ /* push 'package.searchers' to index 3 in the stack */ - if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) + if (l_unlikely(lua_getfield(L, lua_upvalueindex(1), "searchers") + != LUA_TTABLE)) luaL_error(L, "'package.searchers' must be a table"); luaL_buffinit(L, &msg); /* iterate over available searchers to find a loader */ for (i = 1; ; i++) { luaL_addstring(&msg, "\n\t"); /* error-message prefix */ - if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */ + if (l_unlikely(lua_rawgeti(L, 3, i) == LUA_TNIL)) { /* no more searchers? */ lua_pop(L, 1); /* remove nil */ luaL_buffsub(&msg, 2); /* remove prefix */ luaL_pushresult(&msg); /* create error message */ diff --git a/third_party/lua/lobject.h b/third_party/lua/lobject.h index f2c1d613a..33dbba4fb 100644 --- a/third_party/lua/lobject.h +++ b/third_party/lua/lobject.h @@ -135,13 +135,14 @@ typedef struct TValue { ** Entries in a Lua stack. Field 'tbclist' forms a list of all ** to-be-closed variables active in this stack. Dummy entries are ** used when the distance between two tbc variables does not fit -** in an unsigned short. +** in an unsigned short. They are represented by delta==0, and +** their real delta is always the maximum value that fits in +** that field. */ typedef union StackValue { TValue val; struct { TValuefields; - lu_byte isdummy; unsigned short delta; } tbclist; } StackValue; diff --git a/third_party/lua/lopcodes.h b/third_party/lua/lopcodes.h index 4e109c49c..2c5ef28db 100644 --- a/third_party/lua/lopcodes.h +++ b/third_party/lua/lopcodes.h @@ -226,13 +226,13 @@ OP_SELF,/* A B C R[A+1] := R[B]; R[A] := R[B][RK(C):string] */ OP_ADDI,/* A B sC R[A] := R[B] + sC */ -OP_ADDK,/* A B C R[A] := R[B] + K[C] */ -OP_SUBK,/* A B C R[A] := R[B] - K[C] */ -OP_MULK,/* A B C R[A] := R[B] * K[C] */ -OP_MODK,/* A B C R[A] := R[B] % K[C] */ -OP_POWK,/* A B C R[A] := R[B] ^ K[C] */ -OP_DIVK,/* A B C R[A] := R[B] / K[C] */ -OP_IDIVK,/* A B C R[A] := R[B] // K[C] */ +OP_ADDK,/* A B C R[A] := R[B] + K[C]:number */ +OP_SUBK,/* A B C R[A] := R[B] - K[C]:number */ +OP_MULK,/* A B C R[A] := R[B] * K[C]:number */ +OP_MODK,/* A B C R[A] := R[B] % K[C]:number */ +OP_POWK,/* A B C R[A] := R[B] ^ K[C]:number */ +OP_DIVK,/* A B C R[A] := R[B] / K[C]:number */ +OP_IDIVK,/* A B C R[A] := R[B] // K[C]:number */ OP_BANDK,/* A B C R[A] := R[B] & K[C]:integer */ OP_BORK,/* A B C R[A] := R[B] | K[C]:integer */ diff --git a/third_party/lua/loslib.c b/third_party/lua/loslib.c index 92f60255d..1373637fe 100644 --- a/third_party/lua/loslib.c +++ b/third_party/lua/loslib.c @@ -166,7 +166,7 @@ static int os_tmpname (lua_State *L) { char buff[LUA_TMPNAMBUFSIZE]; int err; lua_tmpnam(buff, err); - if (err) + if (l_unlikely(err)) return luaL_error(L, "unable to generate a unique filename"); lua_pushstring(L, buff); return 1; @@ -204,7 +204,7 @@ static int os_clock (lua_State *L) { */ static void setfield (lua_State *L, const char *key, int value, int delta) { #if (defined(LUA_NUMTIME) && LUA_MAXINTEGER <= INT_MAX) - if (value > LUA_MAXINTEGER - delta) + if (l_unlikely(value > LUA_MAXINTEGER - delta)) luaL_error(L, "field '%s' is out-of-bound", key); #endif lua_pushinteger(L, (lua_Integer)value + delta); @@ -249,9 +249,9 @@ static int getfield (lua_State *L, const char *key, int d, int delta) { int t = lua_getfield(L, -1, key); /* get field and its type */ lua_Integer res = lua_tointegerx(L, -1, &isnum); if (!isnum) { /* field is not an integer? */ - if (t != LUA_TNIL) /* some other value? */ + if (l_unlikely(t != LUA_TNIL)) /* some other value? */ return luaL_error(L, "field '%s' is not an integer", key); - else if (d < 0) /* absent field; no default? */ + else if (l_unlikely(d < 0)) /* absent field; no default? */ return luaL_error(L, "field '%s' missing in date table", key); res = d; } diff --git a/third_party/lua/lparser.c b/third_party/lua/lparser.c index 2d9a86a75..4cf90cca2 100644 --- a/third_party/lua/lparser.c +++ b/third_party/lua/lparser.c @@ -123,7 +123,7 @@ static void checknext (LexState *ls, int c) { ** in line 'where' (if that is not the current line). */ static void check_match (LexState *ls, int what, int who, int where) { - if (unlikely(!testnext(ls, what))) { + if (l_unlikely(!testnext(ls, what))) { if (where == ls->linenumber) /* all in the same line? */ error_expected(ls, what); /* do not need a complex message */ else { @@ -512,7 +512,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) { Labellist *gl = &ls->dyd->gt; /* list of goto's */ Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ lua_assert(eqstr(gt->name, label->name)); - if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ + if (l_unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */ jumpscopeerror(ls, gt); luaK_patchlist(ls->fs, gt->pc, label->pc); for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ @@ -1430,7 +1430,7 @@ static void breakstat (LexState *ls) { */ static void checkrepeated (LexState *ls, TString *name) { Labeldesc *lb = findlabel(ls, name); - if (unlikely(lb != NULL)) { /* already defined? */ + if (l_unlikely(lb != NULL)) { /* already defined? */ const char *msg = "label '%s' already defined on line %d"; msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); luaK_semerror(ls, msg); /* error */ @@ -1515,7 +1515,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) { int offset = dest - (pc + 1); if (back) offset = -offset; - if (unlikely(offset > MAXARG_Bx)) + if (l_unlikely(offset > MAXARG_Bx)) luaX_syntaxerror(fs->ls, "control structure too long"); SETARG_Bx(*jmp, offset); } diff --git a/third_party/lua/lstate.c b/third_party/lua/lstate.c index d581bd324..fd0f7152b 100644 --- a/third_party/lua/lstate.c +++ b/third_party/lua/lstate.c @@ -165,7 +165,7 @@ void luaE_checkcstack (lua_State *L) { LUAI_FUNC void luaE_incCstack (lua_State *L) { L->nCcalls++; - if (unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) + if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) luaE_checkcstack(L); } @@ -372,6 +372,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->panic = NULL; g->gcstate = GCSpause; g->gckind = KGC_INC; + g->gcstopem = 0; g->gcemergency = 0; g->finobj = g->tobefnz = g->fixedgc = NULL; g->firstold1 = g->survival = g->old1 = g->reallyold = NULL; diff --git a/third_party/lua/lstate.h b/third_party/lua/lstate.h index 905b3d679..7b5aed28d 100644 --- a/third_party/lua/lstate.h +++ b/third_party/lua/lstate.h @@ -260,6 +260,7 @@ typedef struct global_State { lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ + lu_byte gcstopem; /* stops emergency collections */ lu_byte genminormul; /* control for minor generational collections */ lu_byte genmajormul; /* control for major generational collections */ lu_byte gcrunning; /* true if GC is running */ diff --git a/third_party/lua/lstring.c b/third_party/lua/lstring.c index 6ba8dafe3..3ea66a5f8 100644 --- a/third_party/lua/lstring.c +++ b/third_party/lua/lstring.c @@ -85,7 +85,7 @@ void luaS_resize (lua_State *L, int nsize) { if (nsize < osize) /* shrinking table? */ tablerehash(tb->hash, osize, nsize); /* depopulate shrinking part */ newvect = luaM_reallocvector(L, tb->hash, osize, nsize, TString*); - if (unlikely(newvect == NULL)) { /* reallocation failed? */ + if (l_unlikely(newvect == NULL)) { /* reallocation failed? */ if (nsize < osize) /* was it shrinking table? */ tablerehash(tb->hash, nsize, osize); /* restore to original size */ /* leave table as it was */ @@ -168,7 +168,7 @@ void luaS_remove (lua_State *L, TString *ts) { static void growstrtab (lua_State *L, stringtable *tb) { - if (unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ + if (l_unlikely(tb->nuse == MAX_INT)) { /* too many strings? */ luaC_fullgc(L, 1); /* try to free some... */ if (tb->nuse == MAX_INT) /* still too many? */ luaM_error(L); /* cannot even create a message... */ @@ -219,7 +219,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { return internshrstr(L, str, l); else { TString *ts; - if (unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) + if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char))) luaM_toobig(L); ts = luaS_createlngstrobj(L, l); memcpy(getstr(ts), str, l * sizeof(char)); @@ -255,7 +255,7 @@ Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) { Udata *u; int i; GCObject *o; - if (unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) + if (l_unlikely(s > MAX_SIZE - udatamemoffset(nuvalue))) luaM_toobig(L); o = luaC_newobj(L, LUA_VUSERDATA, sizeudata(nuvalue, s)); u = gco2u(o); diff --git a/third_party/lua/lstrlib.c b/third_party/lua/lstrlib.c index dd5351ea5..0b503196b 100644 --- a/third_party/lua/lstrlib.c +++ b/third_party/lua/lstrlib.c @@ -142,8 +142,9 @@ static int str_rep (lua_State *L) { const char *s = luaL_checklstring(L, 1, &l); lua_Integer n = luaL_checkinteger(L, 2); const char *sep = luaL_optlstring(L, 3, "", &lsep); - if (n <= 0) lua_pushliteral(L, ""); - else if (l + lsep < l || l + lsep > MAXSIZE / n) /* may overflow? */ + if (n <= 0) + lua_pushliteral(L, ""); + else if (l_unlikely(l + lsep < l || l + lsep > MAXSIZE / n)) return luaL_error(L, "resulting string too large"); else { size_t totallen = (size_t)n * l + (size_t)(n - 1) * lsep; @@ -171,7 +172,7 @@ static int str_byte (lua_State *L) { size_t pose = getendpos(L, 3, pi, l); int n, i; if (posi > pose) return 0; /* empty interval; return no values */ - if (pose - posi >= (size_t)INT_MAX) /* arithmetic overflow? */ + if (l_unlikely(pose - posi >= (size_t)INT_MAX)) /* arithmetic overflow? */ return luaL_error(L, "string slice too long"); n = (int)(pose - posi) + 1; luaL_checkstack(L, n, "string slice too long"); @@ -225,7 +226,7 @@ static int str_dump (lua_State *L) { luaL_checktype(L, 1, LUA_TFUNCTION); lua_settop(L, 1); /* ensure function is on the top of the stack */ state.init = 0; - if (lua_dump(L, writer, &state, strip) != 0) + if (l_unlikely(lua_dump(L, writer, &state, strip) != 0)) return luaL_error(L, "unable to dump given function"); luaL_pushresult(&state.B); return 1; @@ -265,7 +266,8 @@ static int tonum (lua_State *L, int arg) { static void trymt (lua_State *L, const char *mtname) { lua_settop(L, 2); /* back to the original arguments */ - if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname)) + if (l_unlikely(lua_type(L, 2) == LUA_TSTRING || + !luaL_getmetafield(L, 2, mtname))) luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2, luaL_typename(L, -2), luaL_typename(L, -1)); lua_insert(L, -3); /* put metamethod before arguments */ @@ -373,7 +375,8 @@ static const char *match (MatchState *ms, const char *s, const char *p); static int check_capture (MatchState *ms, int l) { l -= '1'; - if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) + if (l_unlikely(l < 0 || l >= ms->level || + ms->capture[l].len == CAP_UNFINISHED)) return luaL_error(ms->L, "invalid capture index %%%d", l + 1); return l; } @@ -390,14 +393,14 @@ static int capture_to_close (MatchState *ms) { static const char *classend (MatchState *ms, const char *p) { switch (*p++) { case L_ESC: { - if (p == ms->p_end) + if (l_unlikely(p == ms->p_end)) luaL_error(ms->L, "malformed pattern (ends with '%%')"); return p+1; } case '[': { if (*p == '^') p++; do { /* look for a ']' */ - if (p == ms->p_end) + if (l_unlikely(p == ms->p_end)) luaL_error(ms->L, "malformed pattern (missing ']')"); if (*(p++) == L_ESC && p < ms->p_end) p++; /* skip escapes (e.g. '%]') */ @@ -472,7 +475,7 @@ static int singlematch (MatchState *ms, const char *s, const char *p, static const char *matchbalance (MatchState *ms, const char *s, const char *p) { - if (p >= ms->p_end - 1) + if (l_unlikely(p >= ms->p_end - 1)) luaL_error(ms->L, "malformed pattern (missing arguments to '%%b')"); if (*s != *p) return NULL; else { @@ -555,7 +558,7 @@ static const char *match_capture (MatchState *ms, const char *s, int l) { static const char *match (MatchState *ms, const char *s, const char *p) { - if (ms->matchdepth-- == 0) + if (l_unlikely(ms->matchdepth-- == 0)) luaL_error(ms->L, "pattern too complex"); init: /* using goto's to optimize tail recursion */ if (p != ms->p_end) { /* end of pattern? */ @@ -589,7 +592,7 @@ static const char *match (MatchState *ms, const char *s, const char *p) { case 'f': { /* frontier? */ const char *ep; char previous; p += 2; - if (*p != '[') + if (l_unlikely(*p != '[')) luaL_error(ms->L, "missing '[' after '%%f' in pattern"); ep = classend(ms, p); /* points to what is next */ previous = (s == ms->src_init) ? '\0' : *(s - 1); @@ -689,7 +692,7 @@ static const char *lmemfind (const char *s1, size_t l1, static size_t get_onecapture (MatchState *ms, int i, const char *s, const char *e, const char **cap) { if (i >= ms->level) { - if (i != 0) + if (l_unlikely(i != 0)) luaL_error(ms->L, "invalid capture index %%%d", i + 1); *cap = s; return e - s; @@ -697,7 +700,7 @@ static size_t get_onecapture (MatchState *ms, int i, const char *s, else { ptrdiff_t capl = ms->capture[i].len; *cap = ms->capture[i].init; - if (capl == CAP_UNFINISHED) + if (l_unlikely(capl == CAP_UNFINISHED)) luaL_error(ms->L, "unfinished capture"); else if (capl == CAP_POSITION) lua_pushinteger(ms->L, (ms->capture[i].init - ms->src_init) + 1); @@ -916,7 +919,7 @@ static int add_value (MatchState *ms, luaL_Buffer *b, const char *s, luaL_addlstring(b, s, e - s); /* keep original text */ return 0; /* no changes */ } - else if (!lua_isstring(L, -1)) + else if (l_unlikely(!lua_isstring(L, -1))) return luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); else { @@ -1048,7 +1051,7 @@ static int lua_number2strx (lua_State *L, char *buff, int sz, for (i = 0; i < n; i++) buff[i] = toupper(uchar(buff[i])); } - else if (fmt[SIZELENMOD] != 'a') + else if (l_unlikely(fmt[SIZELENMOD] != 'a')) return luaL_error(L, "modifiers for format '%%a'/'%%A' not implemented"); return n; } @@ -1401,7 +1404,7 @@ static int getnum (const char **fmt, int df) { */ static int getnumlimit (Header *h, const char **fmt, int df) { int sz = getnum(fmt, df); - if (sz > MAXINTSIZE || sz <= 0) + if (l_unlikely(sz > MAXINTSIZE || sz <= 0)) return luaL_error(h->L, "integral size (%d) out of limits [1,%d]", sz, MAXINTSIZE); return sz; @@ -1442,7 +1445,7 @@ static KOption getoption (Header *h, const char **fmt, int *size) { case 's': *size = getnumlimit(h, fmt, sizeof(size_t)); return Kstring; case 'c': *size = getnum(fmt, -1); - if (*size == -1) + if (l_unlikely(*size == -1)) luaL_error(h->L, "missing size for format option 'c'"); return Kchar; case 'z': return Kzstr; @@ -1481,7 +1484,7 @@ static KOption getdetails (Header *h, size_t totalsize, else { if (align > h->maxalign) /* enforce maximum alignment */ align = h->maxalign; - if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ + if (l_unlikely((align & (align - 1)) != 0)) /* not a power of 2? */ luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); } @@ -1673,7 +1676,7 @@ static lua_Integer unpackint (lua_State *L, const char *str, else if (size > SZINT) { /* must check unread bytes */ int mask = (!issigned || (lua_Integer)res >= 0) ? 0 : MC; for (i = limit; i < size; i++) { - if ((unsigned char)str[islittle ? i : size - 1 - i] != mask) + if (l_unlikely((unsigned char)str[islittle ? i : size - 1 - i] != mask)) luaL_error(L, "%d-byte integer does not fit into Lua Integer", size); } } diff --git a/third_party/lua/ltable.c b/third_party/lua/ltable.c index a9f1ee181..4d021a141 100644 --- a/third_party/lua/ltable.c +++ b/third_party/lua/ltable.c @@ -64,20 +64,25 @@ #define MAXHSIZE luaM_limitN(1u << MAXHBITS, Node) +/* +** When the original hash value is good, hashing by a power of 2 +** avoids the cost of '%'. +*/ #define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) +/* +** for other types, it is better to avoid modulo by power of 2, as +** they can have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + #define hashstr(t,str) hashpow2(t, (str)->hash) #define hashboolean(t,p) hashpow2(t, p) + #define hashint(t,i) hashpow2(t, i) -/* -** for some types, it is better to avoid modulus by power of 2, as -** they tend to have many 2 factors. -*/ -#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) - - #define hashpointer(t,p) hashmod(t, point2uint(p)) @@ -131,24 +136,38 @@ static int l_hashfloat (lua_Number n) { */ static Node *mainposition (const Table *t, int ktt, const Value *kvl) { switch (withvariant(ktt)) { - case LUA_VNUMINT: - return hashint(t, ivalueraw(*kvl)); - case LUA_VNUMFLT: - return hashmod(t, l_hashfloat(fltvalueraw(*kvl))); - case LUA_VSHRSTR: - return hashstr(t, tsvalueraw(*kvl)); - case LUA_VLNGSTR: - return hashpow2(t, luaS_hashlongstr(tsvalueraw(*kvl))); + case LUA_VNUMINT: { + lua_Integer key = ivalueraw(*kvl); + return hashint(t, key); + } + case LUA_VNUMFLT: { + lua_Number n = fltvalueraw(*kvl); + return hashmod(t, l_hashfloat(n)); + } + case LUA_VSHRSTR: { + TString *ts = tsvalueraw(*kvl); + return hashstr(t, ts); + } + case LUA_VLNGSTR: { + TString *ts = tsvalueraw(*kvl); + return hashpow2(t, luaS_hashlongstr(ts)); + } case LUA_VFALSE: return hashboolean(t, 0); case LUA_VTRUE: return hashboolean(t, 1); - case LUA_VLIGHTUSERDATA: - return hashpointer(t, pvalueraw(*kvl)); - case LUA_VLCF: - return hashpointer(t, fvalueraw(*kvl)); - default: - return hashpointer(t, gcvalueraw(*kvl)); + case LUA_VLIGHTUSERDATA: { + void *p = pvalueraw(*kvl); + return hashpointer(t, p); + } + case LUA_VLCF: { + lua_CFunction f = fvalueraw(*kvl); + return hashpointer(t, f); + } + default: { + GCObject *o = gcvalueraw(*kvl); + return hashpointer(t, o); + } } } @@ -303,7 +322,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key, return i; /* yes; that's the index */ else { const TValue *n = getgeneric(t, key, 1); - if (unlikely(isabstkey(n))) + if (l_unlikely(isabstkey(n))) luaG_runerror(L, "invalid key to 'next'"); /* key not found */ i = cast_int(nodefromval(n) - gnode(t, 0)); /* key index in hash table */ /* hash elements are numbered after array ones */ @@ -537,7 +556,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, } /* allocate new array */ newarray = luaM_reallocvector(L, t->array, oldasize, newasize, TValue); - if (unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ + if (l_unlikely(newarray == NULL && newasize > 0)) { /* allocation failed? */ freehash(L, &newt); /* release new hash part */ luaM_error(L); /* raise error (with array unchanged) */ } @@ -631,7 +650,7 @@ static Node *getfreepos (Table *t) { void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { Node *mp; TValue aux; - if (unlikely(ttisnil(key))) + if (l_unlikely(ttisnil(key))) luaG_runerror(L, "table index is nil"); else if (ttisfloat(key)) { lua_Number f = fltvalue(key); @@ -640,7 +659,7 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) { setivalue(&aux, k); key = &aux; /* insert it as an integer */ } - else if (unlikely(luai_numisnan(f))) + else if (l_unlikely(luai_numisnan(f))) luaG_runerror(L, "table index is NaN"); } if (ttisnil(value)) diff --git a/third_party/lua/ltablib.c b/third_party/lua/ltablib.c index 7c10eee5f..553f2a7e6 100644 --- a/third_party/lua/ltablib.c +++ b/third_party/lua/ltablib.c @@ -141,7 +141,7 @@ static int tmove (lua_State *L) { static void addfield (lua_State *L, luaL_Buffer *b, lua_Integer i) { lua_geti(L, 1, i); - if (!lua_isstring(L, -1)) + if (l_unlikely(!lua_isstring(L, -1))) luaL_error(L, "invalid value (%s) at index %I in table for 'concat'", luaL_typename(L, -1), i); luaL_addvalue(b); @@ -192,7 +192,8 @@ static int tunpack (lua_State *L) { lua_Integer e = luaL_opt(L, luaL_checkinteger, 3, luaL_len(L, 1)); if (i > e) return 0; /* empty range */ n = (lua_Unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ - if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) + if (l_unlikely(n >= (unsigned int)INT_MAX || + !lua_checkstack(L, (int)(++n)))) return luaL_error(L, "too many results to unpack"); for (; i < e; i++) { /* push arg[i..e - 1] (to avoid overflows) */ lua_geti(L, 1, i); @@ -294,14 +295,14 @@ static IdxT partition (lua_State *L, IdxT lo, IdxT up) { for (;;) { /* next loop: repeat ++i while a[i] < P */ while ((void)lua_geti(L, 1, ++i), sort_comp(L, -1, -2)) { - if (i == up - 1) /* a[i] < P but a[up - 1] == P ?? */ + if (l_unlikely(i == up - 1)) /* a[i] < P but a[up - 1] == P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[i] */ } /* after the loop, a[i] >= P and a[lo .. i - 1] < P */ /* next loop: repeat --j while P < a[j] */ while ((void)lua_geti(L, 1, --j), sort_comp(L, -3, -1)) { - if (j < i) /* j < i but a[j] > P ?? */ + if (l_unlikely(j < i)) /* j < i but a[j] > P ?? */ luaL_error(L, "invalid order function for sorting"); lua_pop(L, 1); /* remove a[j] */ } diff --git a/third_party/lua/ltests.c b/third_party/lua/ltests.c index e12ac0a38..da4ebcb15 100644 --- a/third_party/lua/ltests.c +++ b/third_party/lua/ltests.c @@ -112,6 +112,7 @@ static void warnf (void *ud, const char *msg, int tocont) { strcat(buff, msg); /* add new message to current warning */ if (!tocont) { /* message finished? */ lua_unlock(L); + luaL_checkstack(L, 1, "warn stack space"); lua_getglobal(L, "_WARN"); if (!lua_toboolean(L, -1)) lua_pop(L, 1); /* ok, no previous unexpected warning */ @@ -133,6 +134,7 @@ static void warnf (void *ud, const char *msg, int tocont) { } case 2: { /* store */ lua_unlock(L); + luaL_checkstack(L, 1, "warn stack space"); lua_pushstring(L, buff); lua_setglobal(L, "_WARN"); /* assign message to global '_WARN' */ lua_lock(L); diff --git a/third_party/lua/ltm.c b/third_party/lua/ltm.c index c4da4942a..75f67c5f6 100644 --- a/third_party/lua/ltm.c +++ b/third_party/lua/ltm.c @@ -143,7 +143,7 @@ static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2, void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { - if (!callbinTM(L, p1, p2, res, event)) { + if (l_unlikely(!callbinTM(L, p1, p2, res, event))) { switch (event) { case TM_BAND: case TM_BOR: case TM_BXOR: case TM_SHL: case TM_SHR: case TM_BNOT: { @@ -162,7 +162,8 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, void luaT_tryconcatTM (lua_State *L) { StkId top = L->top; - if (!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, TM_CONCAT)) + if (l_unlikely(!callbinTM(L, s2v(top - 2), s2v(top - 1), top - 2, + TM_CONCAT))) luaG_concaterror(L, s2v(top - 2), s2v(top - 1)); } diff --git a/third_party/lua/lua.h b/third_party/lua/lua.h index 4f24b7829..c988b3def 100644 --- a/third_party/lua/lua.h +++ b/third_party/lua/lua.h @@ -14,14 +14,14 @@ #define LUA_VERSION_MAJOR "5" #define LUA_VERSION_MINOR "4" -#define LUA_VERSION_RELEASE "2" +#define LUA_VERSION_RELEASE "3" #define LUA_VERSION_NUM 504 #define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 0) #define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR #define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE -#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2020 Lua.org, PUC-Rio" +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2021 Lua.org, PUC-Rio" #define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" @@ -488,7 +488,7 @@ struct lua_Debug { /****************************************************************************** -* Copyright (C) 1994-2020 Lua.org, PUC-Rio. +* Copyright (C) 1994-2021 Lua.org, PUC-Rio. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the diff --git a/third_party/lua/luaconf.h b/third_party/lua/luaconf.h index e36d0eb0c..1bc9480fd 100644 --- a/third_party/lua/luaconf.h +++ b/third_party/lua/luaconf.h @@ -659,6 +659,34 @@ #define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) #endif + +/* +** macros to improve jump prediction, used mostly for error handling +** and debug facilities. (Some macros in the Lua API use these macros. +** Define LUA_NOBUILTIN if you do not want '__builtin_expect' in your +** code.) +*/ +#if !defined(luai_likely) + +#if defined(__GNUC__) && !defined(LUA_NOBUILTIN) +#define luai_likely(x) (__builtin_expect(((x) != 0), 1)) +#define luai_unlikely(x) (__builtin_expect(((x) != 0), 0)) +#else +#define luai_likely(x) (x) +#define luai_unlikely(x) (x) +#endif + +#endif + + +#if defined(LUA_CORE) || defined(LUA_LIB) +/* shorter names for Lua's own use */ +#define l_likely(x) luai_likely(x) +#define l_unlikely(x) luai_unlikely(x) +#endif + + + /* }================================================================== */ diff --git a/third_party/lua/lvm.c b/third_party/lua/lvm.c index 2f7b33dd6..7780b974a 100644 --- a/third_party/lua/lvm.c +++ b/third_party/lua/lvm.c @@ -227,11 +227,11 @@ static int forprep (lua_State *L, StkId ra) { } else { /* try making all values floats */ lua_Number init; lua_Number limit; lua_Number step; - if (unlikely(!tonumber(plimit, &limit))) + if (l_unlikely(!tonumber(plimit, &limit))) luaG_forerror(L, plimit, "limit"); - if (unlikely(!tonumber(pstep, &step))) + if (l_unlikely(!tonumber(pstep, &step))) luaG_forerror(L, pstep, "step"); - if (unlikely(!tonumber(pinit, &init))) + if (l_unlikely(!tonumber(pinit, &init))) luaG_forerror(L, pinit, "initial value"); if (step == 0) luaG_runerror(L, "'for' step is zero"); @@ -284,7 +284,7 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, if (slot == NULL) { /* 't' is not a table? */ lua_assert(!ttistable(t)); tm = luaT_gettmbyobj(L, t, TM_INDEX); - if (unlikely(notm(tm))) + if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); /* no metamethod */ /* else will try the metamethod */ } @@ -338,7 +338,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, } else { /* not a table; check metamethod */ tm = luaT_gettmbyobj(L, t, TM_NEWINDEX); - if (unlikely(notm(tm))) + if (l_unlikely(notm(tm))) luaG_typeerror(L, t, "index"); } /* try the metamethod */ @@ -560,8 +560,13 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { if (ttype(t1) != ttype(t2) || ttype(t1) != LUA_TNUMBER) return 0; /* only numbers can be equal with different variants */ else { /* two numbers with different variants */ - lua_Integer i1, i2; /* compare them as integers */ - return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2); + /* One of them is an integer. If the other does not have an + integer value, they cannot be equal; otherwise, compare their + integer values. */ + lua_Integer i1, i2; + return (luaV_tointegerns(t1, &i1, F2Ieq) && + luaV_tointegerns(t2, &i2, F2Ieq) && + i1 == i2); } } /* values have same type and same variant */ @@ -643,7 +648,7 @@ void luaV_concat (lua_State *L, int total) { /* collect total length and number of strings */ for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) { size_t l = vslen(s2v(top - n - 1)); - if (unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) + if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) luaG_runerror(L, "string length overflow"); tl += l; } @@ -687,7 +692,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { } default: { /* try metamethod */ tm = luaT_gettmbyobj(L, rb, TM_LEN); - if (unlikely(notm(tm))) /* no metamethod? */ + if (l_unlikely(notm(tm))) /* no metamethod? */ luaG_typeerror(L, rb, "get length of"); break; } @@ -703,7 +708,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) { ** otherwise 'floor(q) == trunc(q) - 1'. */ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { - if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to divide by zero"); return intop(-, 0, m); /* n==-1; avoid overflow with 0x80000...//-1 */ @@ -723,7 +728,7 @@ lua_Integer luaV_idiv (lua_State *L, lua_Integer m, lua_Integer n) { ** about luaV_idiv.) */ lua_Integer luaV_mod (lua_State *L, lua_Integer m, lua_Integer n) { - if (unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ + if (l_unlikely(l_castS2U(n) + 1u <= 1u)) { /* special cases: -1 or 0 */ if (n == 0) luaG_runerror(L, "attempt to perform 'n%%0'"); return 0; /* m % -1 == 0; avoid overflow with 0x80000...%-1 */ @@ -913,7 +918,7 @@ void luaV_finishOp (lua_State *L) { */ #define op_arithfK(L,fop) { \ TValue *v1 = vRB(i); \ - TValue *v2 = KC(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arithf_aux(L, v1, v2, fop); } @@ -942,7 +947,7 @@ void luaV_finishOp (lua_State *L) { */ #define op_arithK(L,iop,fop) { \ TValue *v1 = vRB(i); \ - TValue *v2 = KC(i); \ + TValue *v2 = KC(i); lua_assert(ttisnumber(v2)); \ op_arith_aux(L, v1, v2, iop, fop); } @@ -1041,7 +1046,8 @@ void luaV_finishOp (lua_State *L) { #define updatebase(ci) (base = ci->func + 1) -#define updatestack(ci) { if (trap) { updatebase(ci); ra = RA(i); } } +#define updatestack(ci) \ + { if (l_unlikely(trap)) { updatebase(ci); ra = RA(i); } } /* @@ -1099,7 +1105,7 @@ void luaV_finishOp (lua_State *L) { /* fetch an instruction and prepare its execution */ #define vmfetch() { \ - if (trap) { /* stack reallocation or hooks? */ \ + if (l_unlikely(trap)) { /* stack reallocation or hooks? */ \ trap = luaG_traceexec(L, pc); /* handle hooks */ \ updatebase(ci); /* correct stack */ \ } \ @@ -1127,7 +1133,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { cl = clLvalue(s2v(ci->func)); k = cl->p->k; pc = ci->u.l.savedpc; - if (trap) { + if (l_unlikely(trap)) { if (pc == cl->p->code) { /* first instruction (not resuming)? */ if (cl->p->is_vararg) trap = 0; /* hooks will start after VARARGPREP instruction */ @@ -1670,23 +1676,23 @@ void luaV_execute (lua_State *L, CallInfo *ci) { goto ret; } vmcase(OP_RETURN0) { - if (L->hookmask) { + if (l_unlikely(L->hookmask)) { L->top = ra; savepc(ci); luaD_poscall(L, ci, 0); /* no hurry... */ trap = 1; } else { /* do the 'poscall' here */ - int nres = ci->nresults; + int nres; L->ci = ci->previous; /* back to caller */ L->top = base - 1; - while (nres-- > 0) + for (nres = ci->nresults; l_unlikely(nres > 0); nres--) setnilvalue(s2v(L->top++)); /* all results are nil */ } goto ret; } vmcase(OP_RETURN1) { - if (L->hookmask) { + if (l_unlikely(L->hookmask)) { L->top = ra + 1; savepc(ci); luaD_poscall(L, ci, 1); /* no hurry... */ @@ -1700,8 +1706,8 @@ void luaV_execute (lua_State *L, CallInfo *ci) { else { setobjs2s(L, base - 1, ra); /* at least this result */ L->top = base; - while (--nres > 0) /* complete missing results */ - setnilvalue(s2v(L->top++)); + for (; l_unlikely(nres > 1); nres--) + setnilvalue(s2v(L->top++)); /* complete missing results */ } } ret: /* return from a Lua function */ @@ -1804,7 +1810,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } vmcase(OP_VARARGPREP) { ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); - if (trap) { + if (l_unlikely(trap)) { /* previous "Protect" updated trap */ luaD_hookcall(L, ci); L->oldpc = 1; /* next opcode will be seen as a "new" line */ } diff --git a/third_party/lua/lvm.h b/third_party/lua/lvm.h index efa7c6e20..8fd25ed22 100644 --- a/third_party/lua/lvm.h +++ b/third_party/lua/lvm.h @@ -60,12 +60,14 @@ typedef enum { /* convert an object to an integer (including string coercion) */ #define tointeger(o,i) \ - (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I)) + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointeger(o,i,LUA_FLOORN2I)) /* convert an object to an integer (without string coercion) */ #define tointegerns(o,i) \ - (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointegerns(o,i,LUA_FLOORN2I)) + (l_likely(ttisinteger(o)) ? (*(i) = ivalue(o), 1) \ + : luaV_tointegerns(o,i,LUA_FLOORN2I)) #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2)) diff --git a/third_party/lua/test/all.lua b/third_party/lua/test/all.lua index ef1ef6efe..25810aff4 100644 --- a/third_party/lua/test/all.lua +++ b/third_party/lua/test/all.lua @@ -154,18 +154,8 @@ end dofile('main.lua') -do - local next, setmetatable, stderr = next, setmetatable, io.stderr - -- track collections - local mt = {} - -- each time a table is collected, remark it for finalization - -- on next cycle - mt.__gc = function (o) - stderr:write'.' -- mark progress - local n = setmetatable(o, mt) -- remark it - end - local n = setmetatable({}, mt) -- create object -end +-- trace GC cycles +require"tracegc".start() report"gc.lua" local f = assert(loadfile('gc.lua')) diff --git a/third_party/lua/test/api.lua b/third_party/lua/test/api.lua index 86487acb2..b9b4312da 100644 --- a/third_party/lua/test/api.lua +++ b/third_party/lua/test/api.lua @@ -1130,7 +1130,7 @@ do -- closing resources with 'closeslot' _ENV.xxx = true local a = T.testC([[ - pushvalue 2 # stack: S, NR, CH + pushvalue 2 # stack: S, NR, CH, NR call 0 1 # create resource; stack: S, NR, CH, R toclose -1 # mark it to be closed pushvalue 2 # stack: S, NR, CH, R, NR @@ -1151,6 +1151,30 @@ do ]], newresource, check) assert(a == 3 and _ENV.xxx == nil) -- no extra items left in the stack + -- closing resources with 'pop' + local a = T.testC([[ + pushvalue 2 # stack: S, NR, CH, NR + call 0 1 # create resource; stack: S, NR, CH, R + toclose -1 # mark it to be closed + pushvalue 2 # stack: S, NR, CH, R, NR + call 0 1 # create another resource; stack: S, NR, CH, R, R + toclose -1 # mark it to be closed + pushvalue 3 # stack: S, NR, CH, R, R, CH + pushint 2 # there should be two open resources + call 1 0 # stack: S, NR, CH, R, R + pop 1 # pop second resource + pushvalue 3 # stack: S, NR, CH, R, CH + pushint 1 # there should be one open resource + call 1 0 # stack: S, NR, CH, R + pop 1 # pop other resource from the stack + pushvalue 3 # stack: S, NR, CH, CH + pushint 0 # there should be no open resources + call 1 0 # stack: S, NR, CH + pushint * + return 1 # return stack size + ]], newresource, check) + assert(a == 3) -- no extra items left in the stack + -- non-closable value local a, b = pcall(T.makeCfunc[[ pushint 32 diff --git a/third_party/lua/test/cstack.lua b/third_party/lua/test/cstack.lua index 39e2ee581..b0f653c79 100644 --- a/third_party/lua/test/cstack.lua +++ b/third_party/lua/test/cstack.lua @@ -2,6 +2,8 @@ -- See Copyright Notice in file all.lua +local tracegc = require"tracegc" + print"testing stack overflow detection" -- Segmentation faults in these tests probably result from a C-stack @@ -21,7 +23,9 @@ do print("testing stack overflow in message handling") count = count + 1 return 1 + loop(x, y, z) end + tracegc.stop() -- __gc should not be called with a full stack local res, msg = xpcall(loop, loop) + tracegc.start() assert(msg == "error in error handling") print("final count: ", count) end @@ -135,18 +139,18 @@ if T then local topB, sizeB -- top and size Before overflow local topA, sizeA -- top and size After overflow topB, sizeB = T.stacklevel() - collectgarbage("stop") -- __gc should not be called with a full stack + tracegc.stop() -- __gc should not be called with a full stack xpcall(f, err) - collectgarbage("restart") + tracegc.start() topA, sizeA = T.stacklevel() -- sizes should be comparable assert(topA == topB and sizeA < sizeB * 2) print(string.format("maximum stack size: %d", stack1)) LIM = N -- will stop recursion at maximum level N = 0 -- to count again - collectgarbage("stop") -- __gc should not be called with a full stack + tracegc.stop() -- __gc should not be called with a full stack f() - collectgarbage("restart") + tracegc.start() print"+" end diff --git a/third_party/lua/test/errors.lua b/third_party/lua/test/errors.lua index 7ae09f213..b1d1b137d 100644 --- a/third_party/lua/test/errors.lua +++ b/third_party/lua/test/errors.lua @@ -191,6 +191,13 @@ checkmessage("a = 24 // 0", "divide by zero") checkmessage("a = 1 % 0", "'n%0'") +-- type error for an object which is neither in an upvalue nor a register. +-- The following code will try to index the value 10 that is stored in +-- the metatable, without moving it to a register. +checkmessage("local a = setmetatable({}, {__index = 10}).x", + "attempt to index a number value") + + -- numeric for loops checkmessage("for i = {}, 10 do end", "table") checkmessage("for i = io.stdin, 10 do end", "FILE") @@ -413,6 +420,14 @@ if not b then end end]], 5) +do + -- Force a negative estimate for base line. Error in instruction 2 + -- (after VARARGPREP, GETGLOBAL), with first absolute line information + -- (forced by too many lines) in instruction 0. + local s = string.format("%s return __A.x", string.rep("\n", 300)) + lineerror(s, 301) +end + if not _soft then -- several tests that exaust the Lua stack diff --git a/third_party/lua/test/gc.lua b/third_party/lua/test/gc.lua index 301af51b8..f6eb7c9f0 100644 --- a/third_party/lua/test/gc.lua +++ b/third_party/lua/test/gc.lua @@ -676,6 +676,14 @@ end -- just to make sure assert(collectgarbage'isrunning') +do -- check that the collector is reentrant in incremental mode + setmetatable({}, {__gc = function () + collectgarbage() + end}) + collectgarbage() +end + + collectgarbage(oldmode) print('OK') diff --git a/third_party/lua/test/locals.lua b/third_party/lua/test/locals.lua index 6336e51f2..2af78913d 100644 --- a/third_party/lua/test/locals.lua +++ b/third_party/lua/test/locals.lua @@ -5,6 +5,8 @@ print('testing local variables and environments') local debug = require"debug" +local tracegc = require"tracegc" + -- bug in 5.1: @@ -554,9 +556,9 @@ do -- test for tbc variable high in the stack obj[1] = 100 flag = obj end) - collectgarbage("stop") + tracegc.stop() st, obj = xpcall(overflow, errorh, 0) - collectgarbage("restart") + tracegc.start() end) co() assert(not st and obj[1] == 10 and flag[1] == 100) diff --git a/third_party/lua/test/packtests b/third_party/lua/test/packtests index c19b74bf3..0dbb92fe5 100755 --- a/third_party/lua/test/packtests +++ b/third_party/lua/test/packtests @@ -33,6 +33,7 @@ $NAME/pm.lua \ $NAME/sort.lua \ $NAME/strings.lua \ $NAME/tpack.lua \ +$NAME/tracegc.lua \ $NAME/utf8.lua \ $NAME/vararg.lua \ $NAME/verybig.lua \ diff --git a/third_party/lua/test/tracegc.lua b/third_party/lua/test/tracegc.lua new file mode 100644 index 000000000..9c5c1b3f5 --- /dev/null +++ b/third_party/lua/test/tracegc.lua @@ -0,0 +1,40 @@ +-- track collections + +local M = {} + +-- import list +local setmetatable, stderr, collectgarbage = + setmetatable, io.stderr, collectgarbage + +_ENV = nil + +local active = false + + +-- each time a table is collected, remark it for finalization on next +-- cycle +local mt = {} +function mt.__gc (o) + stderr:write'.' -- mark progress + if active then + setmetatable(o, mt) -- remark object for finalization + end +end + + +function M.start () + if not active then + active = true + setmetatable({}, mt) -- create initial object + end +end + + +function M.stop () + if active then + active = false + collectgarbage() -- call finalizer for the last time + end +end + +return M