From 6a7b2b97217f862b8d21298d21938bd46fa46607 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:39:19 +0100 Subject: [PATCH 01/17] Blua: micro-optimization: avoid one API call in 'read_chars' https://github.com/lua/lua/commit/f86f4116c82654d24faa4a32a909d0c27f3d4746 --- src/blua/liolib.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/blua/liolib.c b/src/blua/liolib.c index 2cd8dd481..2ef46c6a6 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -356,20 +356,21 @@ static int read_line (lua_State *L, FILE *f) { static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t rlen; /* how much to read */ - size_t nr; /* number of chars actually read */ + size_t tbr = n; /* number of chars to be read */ + size_t rlen; /* how much to read in each cycle */ + size_t nr; /* number of chars actually read in each cycle */ luaL_Buffer b; luaL_buffinit(L, &b); rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ do { char *p = luaL_prepbuffer(&b); - if (rlen > n) rlen = n; /* cannot read more than asked */ + if (rlen > tbr) rlen = tbr; /* cannot read more than asked */ nr = fread(p, sizeof(char), rlen, f); luaL_addsize(&b, nr); - n -= nr; /* still have to read `n' chars */ - } while (n > 0 && nr == rlen); /* until end of count or eof */ + tbr -= nr; /* still have to read 'tbr' chars */ + } while (tbr > 0 && nr == rlen); /* until end of count or eof */ luaL_pushresult(&b); /* close buffer */ - return (n == 0 || lua_objlen(L, -1) > 0); + return (tbr == 0 || tbr < n); /* true iff read something */ } @@ -432,13 +433,13 @@ static int f_read (lua_State *L) { static int io_readline (lua_State *L) { FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1)); - int sucess; + int success; if (f == NULL) /* file is already closed? */ luaL_error(L, "file is already closed"); - sucess = read_line(L, f); + success = read_line(L, f); if (ferror(f)) return luaL_error(L, "%s", strerror(errno)); - if (sucess) return 1; + if (success) return 1; else { /* EOF */ if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ lua_settop(L, 0); -- 2.54.0 From 19b64a8b61a06cd9775e6a7de82c45f116e00cfb Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:39:56 +0100 Subject: [PATCH 02/17] Blua: small optimization (reorder of BinOpr enum to unify some cases in switches) https://github.com/lua/lua/commit/d7872dcf91eb2075312a03973a6cdb489610c796 --- src/blua/lcode.c | 25 +++++++++++++------------ src/blua/lcode.h | 6 +++--- src/blua/lparser.c | 8 ++++---- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/blua/lcode.c b/src/blua/lcode.c index efb20e96b..567b88500 100644 --- a/src/blua/lcode.c +++ b/src/blua/lcode.c @@ -790,18 +790,19 @@ void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { case OPR_BXOR: codearith(fs, OP_BXOR, e1, e2); break; case OPR_BSHL: codearith(fs, OP_BSHL, e1, e2); break; case OPR_BSHR: codearith(fs, OP_BSHR, e1, e2); break; - case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; - case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; - case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; - case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; - case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; - case OPR_POW: codearith(fs, OP_POW, e1, e2); break; - case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; - case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; - case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; - case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; - case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; - case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + codearith(fs, op - OPR_ADD + OP_ADD, e1, e2); + break; + } + case OPR_EQ: case OPR_LT: case OPR_LE: { + codecomp(fs, op - OPR_EQ + OP_EQ, 1, e1, e2); + break; + } + case OPR_NE: case OPR_GT: case OPR_GE: { + codecomp(fs, op - OPR_NE + OP_EQ, 0, e1, e2); + break; + } default: lua_assert(0); } } diff --git a/src/blua/lcode.h b/src/blua/lcode.h index 5b8009472..b15c1bfeb 100644 --- a/src/blua/lcode.h +++ b/src/blua/lcode.h @@ -21,13 +21,13 @@ /* -** grep "ORDER OPR" if you change these enums +** grep "ORDER OPR" if you change these enums (ORDER OP) */ typedef enum BinOpr { OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, OPR_CONCAT, - OPR_NE, OPR_EQ, - OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_EQ, OPR_LT, OPR_LE, + OPR_NE, OPR_GT, OPR_GE, OPR_AND, OPR_OR, OPR_BAND, OPR_BOR, OPR_BXOR, OPR_BSHL, OPR_BSHR, diff --git a/src/blua/lparser.c b/src/blua/lparser.c index 871c9730d..8046b0f1f 100644 --- a/src/blua/lparser.c +++ b/src/blua/lparser.c @@ -881,10 +881,10 @@ static const struct { lu_byte right; /* right priority */ } priority[] = { /* ORDER OPR */ {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ - {10, 9}, {5, 4}, /* power and concat (right associative) */ - {3, 3}, {3, 3}, /* equality and inequality */ - {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ - {2, 2}, {1, 1} /* logical (and/or) */ + {10, 9}, {5, 4}, /* ^, .. (right associative) */ + {3, 3}, {3, 3}, {3, 3}, /* ==, <, <= */ + {3, 3}, {3, 3}, {3, 3}, /* ~=, >, >= */ + {2, 2}, {1, 1} /* and, or */ ,{6, 6}, {6, 6}, {6, 6}, /* bit-wise (band/bor/bxor) */ {7, 7}, {7, 7} /* shl/shr */ }; -- 2.54.0 From 49673137209aedc41f53d37133891d748f7f00b8 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:40:51 +0100 Subject: [PATCH 03/17] Blua: better names for metamethods in debug information https://github.com/lua/lua/commit/becf19feeffeb0bf2297e163da4ccc7b4b5e6b5c --- src/blua/ldebug.c | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/blua/ldebug.c b/src/blua/ldebug.c index 542e209a1..e209fae5d 100644 --- a/src/blua/ldebug.c +++ b/src/blua/ldebug.c @@ -542,16 +542,39 @@ static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + TMS tm = 0; Instruction i; if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) return NULL; /* calling function is not Lua (or is unknown) */ ci--; /* calling function */ i = ci_func(ci)->l.p->code[currentpc(L, ci)]; - if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || - GET_OPCODE(i) == OP_TFORLOOP) - return getobjname(L, ci, GETARG_A(i), name); - else - return NULL; /* no useful name can be found */ + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_TFORLOOP: + return getobjname(L, ci, GETARG_A(i), name); + case OP_GETGLOBAL: + case OP_SELF: + case OP_GETTABLE: tm = TM_INDEX; break; + case OP_SETGLOBAL: + case OP_SETTABLE: tm = TM_NEWINDEX; break; + case OP_EQ: tm = TM_EQ; break; + case OP_ADD: tm = TM_ADD; break; + case OP_SUB: tm = TM_SUB; break; + case OP_MUL: tm = TM_MUL; break; + case OP_DIV: tm = TM_DIV; break; + case OP_MOD: tm = TM_MOD; break; + case OP_POW: tm = TM_POW; break; + case OP_UNM: tm = TM_UNM; break; + case OP_LEN: tm = TM_LEN; break; + case OP_LT: tm = TM_LT; break; + case OP_LE: tm = TM_LE; break; + case OP_CONCAT: tm = TM_CONCAT; break; + default: + return NULL; /* else no useful name can be found */ + } + *name = getstr(G(L)->tmname[tm]); + return "metamethod"; } -- 2.54.0 From 347000a32685ad568fa70b77516af9fb82a426a0 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:41:42 +0100 Subject: [PATCH 04/17] Blua: small optimization in luaL_addlstring (avoid adding chars one by one) https://github.com/lua/lua/commit/f292760f12022a83cf01e788482a264aeeb3c276 --- src/blua/lauxlib.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/blua/lauxlib.c b/src/blua/lauxlib.c index 5fb454d5e..51605eaf1 100644 --- a/src/blua/lauxlib.c +++ b/src/blua/lauxlib.c @@ -20,6 +20,7 @@ #define lauxlib_c #define LUA_LIB +#include "ldebug.h" #include "lua.h" #include "lauxlib.h" @@ -468,8 +469,19 @@ LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { - while (l--) - luaL_addchar(B, *s++); + while (l) { + size_t space = bufffree(B); + if (space == 0) { + luaL_prepbuffer(B); + lua_assert(bufffree(B) == LUAL_BUFFERSIZE); + space = LUAL_BUFFERSIZE; + } + if (space > l) space = l; + memcpy(B->p, s, space); + B->p += space; + s += space; + l -= space; + } } -- 2.54.0 From 916cc9d8ca921d1e7b3067d8485e187bc9c4cb8d Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 18:05:51 +0100 Subject: [PATCH 05/17] Blua: implementation of fast track for gettable operations https://github.com/lua/lua/commit/e247c3ada3ff47a1352927e797263137f4e02e0e lets hope i converted this properly lul --- src/blua/lapi.c | 26 ++++++++++++++++++-------- src/blua/ltm.c | 2 +- src/blua/ltm.h | 2 +- src/blua/lvm.c | 45 +++++++++++++++++++++++---------------------- src/blua/lvm.h | 29 ++++++++++++++++++++++++++--- 5 files changed, 69 insertions(+), 35 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index 557896f43..f9cb7cef6 100644 --- a/src/blua/lapi.c +++ b/src/blua/lapi.c @@ -519,6 +519,23 @@ LUA_API int lua_pushthread (lua_State *L) { ** get functions (Lua -> stack) */ +static void auxgetstr (lua_State *L, const TValue *t, const char *k) { + const TValue *aux; + TValue key; + TString *str = luaS_new(L, k); + api_checkvalidindex(L, t); + if (luaV_fastget(L, t, str, aux, luaH_getstr)) { + setobj2s(L, L->top, aux); + api_incr_top(L); + } + else { + setsvalue(L, &key, str); + luaV_finishget(L, t, L->top, L->top, aux); + api_incr_top(L); + } + lua_unlock(L); +} + LUA_API void lua_gettable (lua_State *L, int idx) { StkId t; @@ -531,15 +548,8 @@ LUA_API void lua_gettable (lua_State *L, int idx) { LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { - StkId t; - TValue key; lua_lock(L); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - setsvalue(L, &key, luaS_new(L, k)); - luaV_gettable(L, t, &key, L->top); - api_incr_top(L); - lua_unlock(L); + auxgetstr(L, index2adr(L, idx), k); } diff --git a/src/blua/ltm.c b/src/blua/ltm.c index 031a2811e..22add1891 100644 --- a/src/blua/ltm.c +++ b/src/blua/ltm.c @@ -61,7 +61,7 @@ TValue *luaT_gettm (Table *events, TMS event, TString *ename) { } -TValue *luaT_gettmbyobj (lua_State *L, TValue *o, TMS event) { +TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { Table *mt; switch (ttype(o)) { case LUA_TTABLE: diff --git a/src/blua/ltm.h b/src/blua/ltm.h index 3d4ac8c0e..08b07b20a 100644 --- a/src/blua/ltm.h +++ b/src/blua/ltm.h @@ -55,7 +55,7 @@ LUAI_DATA const char *const luaT_typenames[]; LUAI_FUNC TValue *luaT_gettm (Table *events, TMS event, TString *ename); -LUAI_FUNC TValue *luaT_gettmbyobj (lua_State *L, TValue *o, TMS event); +LUAI_FUNC TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event); LUAI_FUNC void luaT_init (lua_State *L); #endif diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 02d2265c1..629a87591 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -105,33 +105,34 @@ static void callTM (lua_State *L, const TValue *f, const TValue *p1, } -void luaV_gettable (lua_State *L, TValue *t, TValue *key, StkId val) { - int loop; +/* +** Complete a table access: if 't' is a table, 'tm' has its metamethod; +** otherwise, 'tm' is NULL. +*/ +void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, + const TValue *tm) { + int loop; /* counter to avoid infinite loops */ + lua_assert(tm != NULL || !ttistable(t)); for (loop = 0; loop < MAXTAGLOOP; loop++) { - TValue *tm; - if (ttistable(t)) { /* `t' is a table? */ - Table *h = hvalue(t); - const TValue *res = luaH_get(h, key); /* do a primitive get */ - if (!ttisnil(res) || /* result is no nil? */ - (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ - setobj2s(L, val, res); - return; - } - /* else will try the tag method */ + if (tm == NULL) { /* no metamethod (from a table)? */ + if (l_unlikely(ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))) + luaG_typeerror(L, t, "index"); /* no metamethod */ } - else if (l_unlikely(ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))) - luaG_typeerror(L, t, "index"); - if (ttisfunction(tm)) { - callTMres(L, val, tm, t, key); + if (ttisfunction(tm)) { /* metamethod is a function */ + callTMres(L, val, tm, t, key); /* call it */ return; } - t = tm; /* else repeat with `tm' */ + t = tm; /* else repeat access over 'tm' */ + if (luaV_fastget(L,t,key,tm,luaH_get)) { /* try fast track */ + setobj2s(L, val, tm); /* done */ + return; + } + /* else repeat */ } - luaG_runerror(L, "loop in gettable"); + luaG_runerror(L, "gettable chain too long; possible loop"); } - -void luaV_settable (lua_State *L, TValue *t, TValue *key, StkId val) { +void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { TValue *tm = NULL; @@ -159,7 +160,7 @@ void luaV_settable (lua_State *L, TValue *t, TValue *key, StkId val) { } -static int call_binTM (lua_State *L, TValue *p1, TValue *p2, +static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (ttisnil(tm)) @@ -184,7 +185,7 @@ static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, } -static int call_orderTM (lua_State *L, TValue *p1, TValue *p2, +static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, TMS event) { const TValue *tm1 = luaT_gettmbyobj(L, p1, event); const TValue *tm2; diff --git a/src/blua/lvm.h b/src/blua/lvm.h index f9226ee5d..345dc0d6a 100644 --- a/src/blua/lvm.h +++ b/src/blua/lvm.h @@ -22,13 +22,36 @@ (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) +/* +** fast track for 'gettable': 1 means 'aux' points to resulted value; +** 0 means 'aux' is metamethod (if 't' is a table) or NULL. 'f' is +** the raw get function to use. +*/ +#define luaV_fastget(L,t,k,aux,f) \ + (!ttistable(t) \ + ? (aux = NULL, 0) /* not a table; 'aux' is NULL and result is 0 */ \ + : (aux = f(hvalue(t), k), /* else, do raw access */ \ + !ttisnil(aux) ? 1 /* result not nil? 'aux' has it */ \ + : (aux = fasttm(L, hvalue(t)->metatable, TM_INDEX), /* get metamethod */\ + aux != NULL ? 0 /* has metamethod? must call it */ \ + : (aux = luaO_nilobject, 1)))) /* else, final result is nil */ + +/* +** standard implementation for 'gettable' +*/ +#define luaV_gettable(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else luaV_finishget(L,t,k,v,aux); } + + + LUAI_FUNC int luaV_lessthan (lua_State *L, TValue *l, TValue *r); LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); -LUAI_FUNC void luaV_gettable (lua_State *L, TValue *t, TValue *key, - StkId val); -LUAI_FUNC void luaV_settable (lua_State *L, TValue *t, TValue *key, +LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *tm); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val); LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); -- 2.54.0 From b8385d1579e3a101db5cda5360f3a03f08d1394c Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:00:29 +0100 Subject: [PATCH 06/17] Blua: fast track for 'settable' https://github.com/lua/lua/commit/3b795541c488c7e633567897c9112312007a20a0 https://github.com/lua/lua/commit/b91bc93fd33f2158745b37c9e091c6fa4d1a4fd5 had to adjust some things, hope this doesent explode lmao --- src/blua/lapi.c | 29 ++++++++++++++++++++--------- src/blua/lvm.c | 35 ++++++++++++++++++++++++----------- src/blua/lvm.h | 30 ++++++++++++++++++++++++++++-- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index f9cb7cef6..3eeffaa98 100644 --- a/src/blua/lapi.c +++ b/src/blua/lapi.c @@ -641,6 +641,25 @@ LUA_API void lua_getfenv (lua_State *L, int idx) { */ +static void auxsetstr (lua_State *L, const TValue *t, const char *k) { + TValue key; + const TValue *aux; + TString *str = luaS_new(L, k); + api_checknelems(L, 1); + api_checkvalidindex(L, t); + if (luaV_fastset(L, t, str, aux, luaH_setstr, L->top)) { + setobj2t(L, cast(TValue *, aux), L->top - 1); + L->top--; /* pop value */ + } + else { + setsvalue2s(L, &key, str); + luaV_finishset(L, t, &key, L->top - 1, aux); + L->top--; /* pop value */ + } + lua_unlock(L); +} + + LUA_API void lua_settable (lua_State *L, int idx) { StkId t; lua_lock(L); @@ -654,16 +673,8 @@ LUA_API void lua_settable (lua_State *L, int idx) { LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { - StkId t; - TValue key; lua_lock(L); - api_checknelems(L, 1); - t = index2adr(L, idx); - api_checkvalidindex(L, t); - setsvalue(L, &key, luaS_new(L, k)); - luaV_settable(L, t, &key, L->top - 1); - L->top--; /* pop value */ - lua_unlock(L); + auxsetstr(L, index2adr(L, idx), k); } diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 629a87591..824104c9c 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -132,31 +132,44 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, luaG_runerror(L, "gettable chain too long; possible loop"); } -void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { - int loop; +/* +** Main function for table assignment (invoking metamethods if needed). +** Compute 't[key] = val' +*/ +void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval) { + int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - TValue *tm = NULL; - if (ttistable(t)) { /* `t' is a table? */ + const TValue *tm; + if (oldval != NULL) { + lua_assert(ttistable(t) && ttisnil(oldval)); Table *h = hvalue(t); - TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ + /* must check the metamethod */ /* oldval is nil=> look for newindex, oldval is not nil => look for usedindex */ if (!((ttisnil(oldval) && ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) != NULL)) || ((!ttisnil(oldval)) && ((tm = fasttm(L, h->metatable, TM_USEDINDEX)) != NULL)))) { - setobj2t(L, oldval, val); + /* no metamethod and (now) there is an entry with given key */ + setobj2t(L, cast(TValue *, oldval), val); luaC_barriert(L, h, val); return; } - /* else will try the tag method */ + /* else will try the metamethod */ } - else if (l_unlikely(ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))) - luaG_typeerror(L, t, "index"); + else { /* not a table; check metamethod */ + if (l_unlikely(ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))) + luaG_typeerror(L, t, "index"); + } + /* try the metamethod */ if (ttisfunction(tm)) { callTM(L, tm, t, key, val); return; } - t = tm; /* else repeat with `tm' */ + t = tm; /* else repeat assignment over 'tm' */ + if (luaV_fastset(L, t, key, oldval, luaH_set, val)) + return; /* done */ + /* else loop */ } - luaG_runerror(L, "loop in settable"); + luaG_runerror(L, "settable chain too long; possible loop"); } diff --git a/src/blua/lvm.h b/src/blua/lvm.h index 345dc0d6a..57f751a3d 100644 --- a/src/blua/lvm.h +++ b/src/blua/lvm.h @@ -44,6 +44,32 @@ else luaV_finishget(L,t,k,v,aux); } +/* +** Fast track for set table. If 't' is a table and 't[k]' is not nil, +** call GC barrier, do a raw 't[k]=v', and return true; otherwise, +** return false with 'slot' equal to NULL (if 't' is not a table) or +** 'nil'. (This is needed by 'luaV_finishget'.) Note that, if the macro +** returns true, there is no need to 'invalidateTMcache', because the +** call is not creating a new entry. +*/ +#define luaV_fastset(L,t,k,slot,f,v) \ + (!ttistable(t) \ + ? (slot = NULL, 0) \ + : (slot = f(L, hvalue(t), k), \ + ttisnil(slot) ? 0 \ + : (__extension__ ({ \ + if (valiswhite(v) && isblack(obj2gco(hvalue(t)))) \ + luaC_barrierback(L, hvalue(t)); \ + setobj2t(L, cast(TValue *,slot), v); \ + 1; \ + })))) + + +#define luaV_settable(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_set,v)) \ + luaV_finishset(L,t,k,v,slot); } + + LUAI_FUNC int luaV_lessthan (lua_State *L, TValue *l, TValue *r); LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); @@ -51,8 +77,8 @@ LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *tm); -LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, - StkId val); +LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key, + StkId val, const TValue *oldval); LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); -- 2.54.0 From 63026cc11d9aeb3a7b3c8ac9b165377b0057b57a Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:12:59 +0100 Subject: [PATCH 07/17] Blua: 'Protect' in table operations is not needed in the fast track https://github.com/lua/lua/commit/df8b996bccd3830d6fe9d7ba80e8fccd6ac3cabf --- src/blua/lvm.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 824104c9c..6cb76ce30 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -391,6 +391,21 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, } +/* +** copy of 'luaV_gettable', but protecting call to potential metamethod +** (which can reallocate the stack) +*/ +#define gettableProtected(L,t,k,v) { const TValue *aux; \ + if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ + else Protect(luaV_finishget(L,t,k,v,aux)); } + + +/* same for 'luaV_settable' */ +#define settableProtected(L,t,k,v) { const TValue *slot; \ + if (!luaV_fastset(L,t,k,slot,luaH_set,v)) \ + Protect(luaV_finishset(L,t,k,v,slot)); } + + void luaV_execute (lua_State *L, int nexeccalls) { LClosure *cl; @@ -400,9 +415,9 @@ void luaV_execute (lua_State *L, int nexeccalls) { reentry: /* entry point */ lua_assert(isLua(L->ci)); pc = L->savedpc; - cl = &clvalue(L->ci->func)->l; - base = L->base; - k = cl->p->k; + cl = &clvalue(L->ci->func)->l; /* local reference to function's closure */ + k = cl->p->k; /* local reference to function's constant table */ + base = L->base; /* local copy of function's base */ /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; @@ -456,7 +471,9 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_GETTABLE: { - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + StkId rb = RB(i); + TValue *rc = RKC(i); + gettableProtected(L, rb, rc, ra); continue; } case OP_SETGLOBAL: { @@ -473,7 +490,9 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SETTABLE: { - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + TValue *rb = RKB(i); + TValue *rc = RKC(i); + settableProtected(L, ra, rb, rc); continue; } case OP_NEWTABLE: { @@ -485,8 +504,9 @@ void luaV_execute (lua_State *L, int nexeccalls) { } case OP_SELF: { StkId rb = RB(i); - setobjs2s(L, ra+1, rb); - Protect(luaV_gettable(L, rb, RKC(i), ra)); + TValue *rc = RKC(i); + setobjs2s(L, ra + 1, rb); + gettableProtected(L, rb, rc, ra); continue; } case OP_ADD: { -- 2.54.0 From b4277c88c18ccea8413e652569d086656075dab8 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:21:36 +0100 Subject: [PATCH 08/17] Blua: 'luaV_fastget' only treats the real fast case (table with a non-nil value at given key, so that it does not need to check metamethods) https://github.com/lua/lua/commit/1a44e822009752513ce895b9eabc51a4ee4a195a --- src/blua/lapi.c | 16 ++++++------ src/blua/lvm.c | 66 ++++++++++++++++++++++++++++++------------------- src/blua/lvm.h | 26 +++++++++---------- 3 files changed, 61 insertions(+), 47 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index 3eeffaa98..fb1fe2898 100644 --- a/src/blua/lapi.c +++ b/src/blua/lapi.c @@ -520,17 +520,17 @@ LUA_API int lua_pushthread (lua_State *L) { */ static void auxgetstr (lua_State *L, const TValue *t, const char *k) { - const TValue *aux; + const TValue *slot; TValue key; TString *str = luaS_new(L, k); api_checkvalidindex(L, t); - if (luaV_fastget(L, t, str, aux, luaH_getstr)) { - setobj2s(L, L->top, aux); + if (luaV_fastget(L, t, str, slot, luaH_getstr)) { + setobj2s(L, L->top, slot); api_incr_top(L); } else { setsvalue(L, &key, str); - luaV_finishget(L, t, L->top, L->top, aux); + luaV_finishget(L, t, L->top, L->top, slot); api_incr_top(L); } lua_unlock(L); @@ -643,17 +643,17 @@ LUA_API void lua_getfenv (lua_State *L, int idx) { static void auxsetstr (lua_State *L, const TValue *t, const char *k) { TValue key; - const TValue *aux; + const TValue *slot; TString *str = luaS_new(L, k); api_checknelems(L, 1); api_checkvalidindex(L, t); - if (luaV_fastset(L, t, str, aux, luaH_setstr, L->top)) { - setobj2t(L, cast(TValue *, aux), L->top - 1); + if (luaV_fastset(L, t, str, slot, luaH_setstr, L->top)) { + setobj2t(L, cast(TValue *, slot), L->top - 1); L->top--; /* pop value */ } else { setsvalue2s(L, &key, str); - luaV_finishset(L, t, &key, L->top - 1, aux); + luaV_finishset(L, t, &key, L->top - 1, slot); L->top--; /* pop value */ } lua_unlock(L); diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 6cb76ce30..c26e073c9 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -106,50 +106,66 @@ static void callTM (lua_State *L, const TValue *f, const TValue *p1, /* -** Complete a table access: if 't' is a table, 'tm' has its metamethod; -** otherwise, 'tm' is NULL. +** Finish the table access 'val = t[key]'. +** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to +** t[k] entry (which must be nil). */ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, - const TValue *tm) { + const TValue *slot) { int loop; /* counter to avoid infinite loops */ - lua_assert(tm != NULL || !ttistable(t)); + const TValue *tm; /* metamethod */ for (loop = 0; loop < MAXTAGLOOP; loop++) { - if (tm == NULL) { /* no metamethod (from a table)? */ - if (l_unlikely(ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))) + if (slot == NULL) { /* 't' is not a table? */ + lua_assert(!ttistable(t)); + tm = luaT_gettmbyobj(L, t, TM_INDEX); + if (l_unlikely(ttisnil(tm))) luaG_typeerror(L, t, "index"); /* no metamethod */ + /* else will try the metamethod */ } - if (ttisfunction(tm)) { /* metamethod is a function */ + else { /* 't' is a table */ + lua_assert(ttisnil(slot)); + tm = fasttm(L, hvalue(t)->metatable, TM_INDEX); /* table's metamethod */ + if (tm == NULL) { /* no metamethod? */ + setnilvalue(val); /* result is nil */ + return; + } + /* else will try the metamethod */ + } + if (ttisfunction(tm)) { /* is metamethod a function? */ callTMres(L, val, tm, t, key); /* call it */ return; } - t = tm; /* else repeat access over 'tm' */ - if (luaV_fastget(L,t,key,tm,luaH_get)) { /* try fast track */ - setobj2s(L, val, tm); /* done */ + t = tm; /* else try to access 'tm[key]' */ + if (luaV_fastget(L,t,key,slot,luaH_get)) { /* fast track? */ + setobj2s(L, val, slot); /* done */ return; } - /* else repeat */ + /* else repeat (tail call 'luaV_finishget') */ } - luaG_runerror(L, "gettable chain too long; possible loop"); + luaG_runerror(L, "'__index' chain too long; possible loop"); } /* -** Main function for table assignment (invoking metamethods if needed). -** Compute 't[key] = val' +** Finish a table assignment 't[key] = val'. +** If 'slot' is NULL, 't' is not a table. Otherwise, 'slot' points +** to the entry 't[key]', or to 'luaO_nilobject' if there is no such +** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastset' +** would have done the job.) */ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, - StkId val, const TValue *oldval) { + StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { const TValue *tm; - if (oldval != NULL) { - lua_assert(ttistable(t) && ttisnil(oldval)); + if (slot != NULL) { + lua_assert(ttistable(t) && ttisnil(slot)); Table *h = hvalue(t); /* must check the metamethod */ - /* oldval is nil=> look for newindex, oldval is not nil => look for usedindex */ - if (!((ttisnil(oldval) && ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) != NULL)) || - ((!ttisnil(oldval)) && ((tm = fasttm(L, h->metatable, TM_USEDINDEX)) != NULL)))) { + /* slot is nil=> look for newindex, slot is not nil => look for usedindex */ + if (!((ttisnil(slot) && ((tm = fasttm(L, h->metatable, TM_NEWINDEX)) != NULL)) || + ((!ttisnil(slot)) && ((tm = fasttm(L, h->metatable, TM_USEDINDEX)) != NULL)))) { /* no metamethod and (now) there is an entry with given key */ - setobj2t(L, cast(TValue *, oldval), val); + setobj2t(L, cast(TValue *, slot), val); luaC_barriert(L, h, val); return; } @@ -165,7 +181,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, return; } t = tm; /* else repeat assignment over 'tm' */ - if (luaV_fastset(L, t, key, oldval, luaH_set, val)) + if (luaV_fastset(L, t, key, slot, luaH_set, val)) return; /* done */ /* else loop */ } @@ -395,9 +411,9 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, ** copy of 'luaV_gettable', but protecting call to potential metamethod ** (which can reallocate the stack) */ -#define gettableProtected(L,t,k,v) { const TValue *aux; \ - if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ - else Protect(luaV_finishget(L,t,k,v,aux)); } +#define gettableProtected(L,t,k,v) { const TValue *slot; \ + if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ + else Protect(luaV_finishget(L,t,k,v,slot)); } /* same for 'luaV_settable' */ diff --git a/src/blua/lvm.h b/src/blua/lvm.h index 57f751a3d..a14ce827e 100644 --- a/src/blua/lvm.h +++ b/src/blua/lvm.h @@ -23,25 +23,24 @@ /* -** fast track for 'gettable': 1 means 'aux' points to resulted value; -** 0 means 'aux' is metamethod (if 't' is a table) or NULL. 'f' is -** the raw get function to use. +** fast track for 'gettable': if 't' is a table and 't[k]' is not nil, +** return 1 with 'slot' pointing to 't[k]' (final result). Otherwise, +** return 0 (meaning it will have to check metamethod) with 'slot' +** pointing to a nil 't[k]' (if 't' is a table) or NULL (otherwise). +** 'f' is the raw get function to use. */ -#define luaV_fastget(L,t,k,aux,f) \ +#define luaV_fastget(L,t,k,slot,f) \ (!ttistable(t) \ - ? (aux = NULL, 0) /* not a table; 'aux' is NULL and result is 0 */ \ - : (aux = f(hvalue(t), k), /* else, do raw access */ \ - !ttisnil(aux) ? 1 /* result not nil? 'aux' has it */ \ - : (aux = fasttm(L, hvalue(t)->metatable, TM_INDEX), /* get metamethod */\ - aux != NULL ? 0 /* has metamethod? must call it */ \ - : (aux = luaO_nilobject, 1)))) /* else, final result is nil */ + ? (slot = NULL, 0) /* not a table; 'slot' is NULL and result is 0 */ \ + : (slot = f(hvalue(t), k), /* else, do raw access */ \ + !ttisnil(slot))) /* result not nil? */ /* ** standard implementation for 'gettable' */ -#define luaV_gettable(L,t,k,v) { const TValue *aux; \ - if (luaV_fastget(L,t,k,aux,luaH_get)) { setobj2s(L, v, aux); } \ - else luaV_finishget(L,t,k,v,aux); } +#define luaV_gettable(L,t,k,v) { const TValue *slot; \ + if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ + else luaV_finishget(L,t,k,v,slot); } /* @@ -64,7 +63,6 @@ 1; \ })))) - #define luaV_settable(L,t,k,v) { const TValue *slot; \ if (!luaV_fastset(L,t,k,slot,luaH_set,v)) \ luaV_finishset(L,t,k,v,slot); } -- 2.54.0 From 771152b919f47847a760961e416f04654b1a1281 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:23:45 +0100 Subject: [PATCH 09/17] Blua: OP_SELF can use 'luaV_fastget' specialized for strings, as it is applied only to string keys https://github.com/lua/lua/commit/257961c601fdec1ea1f9640d460e0d31c39333a1 --- src/blua/lvm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index c26e073c9..b43bbac72 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -519,10 +519,15 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SELF: { + const TValue *slot; StkId rb = RB(i); TValue *rc = RKC(i); + TString *key = rawtsvalue(rc); /* key must be a string */ setobjs2s(L, ra + 1, rb); - gettableProtected(L, rb, rc, ra); + if (luaV_fastget(L, rb, key, slot, luaH_getstr)) { + setobj2s(L, ra, slot); + } + else Protect(luaV_finishget(L, rb, rc, ra, slot)); continue; } case OP_ADD: { -- 2.54.0 From 8bcada1fda32df7d7f901b74e65c57508a259b71 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:28:57 +0100 Subject: [PATCH 10/17] Blua: opcode OP_SELF operate only with string keys, so they can use fast-track table access https://github.com/lua/lua/commit/cb3d5dce30089512085f78a0bef79e30ef732e30 --- src/blua/lcode.c | 12 ++++++++++++ src/blua/lopcodes.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/src/blua/lcode.c b/src/blua/lcode.c index 567b88500..562ad3506 100644 --- a/src/blua/lcode.c +++ b/src/blua/lcode.c @@ -626,8 +626,20 @@ static void codenot (FuncState *fs, expdesc *e) { removevalues(fs, e->t); } +/* +** Check whether expression 'e' is a literal string +*/ +static int isKstr (FuncState *fs, expdesc *e) { + return (e->k == VK && ttisstring(&fs->f->k[e->u.s.info])); +} +/* +** Create expression 't[k]'. 't' must have its final result already in a +** register or upvalue. Upvalues can only be indexed by literal strings. +*/ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non string? */ + luaK_exp2anyreg(fs, t); /* put it in a register */ t->u.s.aux = luaK_exp2RK(fs, k); t->k = VINDEXED; } diff --git a/src/blua/lopcodes.h b/src/blua/lopcodes.h index 8b08b04e0..aa84da8d0 100644 --- a/src/blua/lopcodes.h +++ b/src/blua/lopcodes.h @@ -232,6 +232,8 @@ OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ (*) In OP_SETLIST, if (B == 0) then B = `top'; if (C == 0) then next `instruction' is real C + (*) In OP_SELF, the index must be a string. + (*) For comparisons, A specifies what condition the test should accept (true or false). -- 2.54.0 From 8e5633a23f0b96a3fd6c00e4cc6c689bc47572df Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 20:09:24 +0100 Subject: [PATCH 11/17] Blua: supress "-Wcast-qual" warnings from intentional casts --- src/blua/lapi.c | 5 ++++- src/blua/lvm.c | 8 ++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index fb1fe2898..0c78bc851 100644 --- a/src/blua/lapi.c +++ b/src/blua/lapi.c @@ -640,7 +640,8 @@ LUA_API void lua_getfenv (lua_State *L, int idx) { ** set functions (stack -> Lua) */ - +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" static void auxsetstr (lua_State *L, const TValue *t, const char *k) { TValue key; const TValue *slot; @@ -671,6 +672,8 @@ LUA_API void lua_settable (lua_State *L, int idx) { lua_unlock(L); } +#pragma GCC diagnostic pop + LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { lua_lock(L); diff --git a/src/blua/lvm.c b/src/blua/lvm.c index b43bbac72..dddd927cf 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -152,6 +152,8 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val, ** entry. (The value at 'slot' must be nil, otherwise 'luaV_fastset' ** would have done the job.) */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" void luaV_finishset (lua_State *L, const TValue *t, TValue *key, StkId val, const TValue *slot) { int loop; /* counter to avoid infinite loops */ @@ -187,6 +189,7 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key, } luaG_runerror(L, "settable chain too long; possible loop"); } +#pragma GCC diagnostic pop static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, @@ -407,6 +410,9 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" + /* ** copy of 'luaV_gettable', but protecting call to potential metamethod ** (which can reallocate the stack) @@ -877,3 +883,5 @@ void luaV_execute (lua_State *L, int nexeccalls) { } } } + +#pragma GCC diagnostic pop -- 2.54.0 From b2b0ca17d3505e20db8f5eca4c471e30a9412344 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 11 Feb 2026 13:44:18 +0100 Subject: [PATCH 12/17] lua branching hint stuff rechecked against lua 5.4 --- src/blua/lauxlib.c | 2 +- src/blua/ldo.c | 4 ++-- src/blua/lstring.c | 4 ++-- src/blua/lvm.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/blua/lauxlib.c b/src/blua/lauxlib.c index 51605eaf1..05a115002 100644 --- a/src/blua/lauxlib.c +++ b/src/blua/lauxlib.c @@ -698,6 +698,6 @@ static int panic (lua_State *L) { LUALIB_API lua_State *luaL_newstate (void) { lua_State *L = lua_newstate(l_alloc, NULL); - if (L) lua_atpanic(L, &panic); + if (l_likely(L)) lua_atpanic(L, &panic); return L; } diff --git a/src/blua/ldo.c b/src/blua/ldo.c index c573dab17..1bda07f11 100644 --- a/src/blua/ldo.c +++ b/src/blua/ldo.c @@ -164,7 +164,7 @@ void luaD_reallocCI (lua_State *L, int newsize) { void luaD_growstack (lua_State *L, int n) { - if (l_likely(n <= L->stacksize)) /* double size is enough? */ + if (n <= L->stacksize) /* double size is enough? */ luaD_reallocstack(L, 2*L->stacksize); else luaD_reallocstack(L, L->stacksize + n); @@ -318,7 +318,7 @@ int luaD_precall (lua_State *L, StkId func, int nresults) { ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->nresults = nresults; - if (L->hookmask & LUA_MASKCALL) + if (l_unlikely(L->hookmask & LUA_MASKCALL)) luaD_callhook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*curr_func(L)->c.f)(L); /* do the actual call */ diff --git a/src/blua/lstring.c b/src/blua/lstring.c index 0bcc30877..44086dd28 100644 --- a/src/blua/lstring.c +++ b/src/blua/lstring.c @@ -51,7 +51,7 @@ static TString *newlstr (lua_State *L, const char *str, size_t l, unsigned int h) { TString *ts; stringtable *tb; - if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + if (l_unlikely(l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))) luaM_toobig(L); ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); ts->tsv.len = l; @@ -83,7 +83,7 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { o != NULL; o = o->gch.next) { TString *ts = rawgco2ts(o); - if (l_unlikely(ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0))) { + if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { /* string may be dead */ if (isdead(G(L), o)) changewhite(o); return ts; diff --git a/src/blua/lvm.c b/src/blua/lvm.c index dddd927cf..0d54a1757 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -28,7 +28,7 @@ -/* limit for table tag-method chains (to avoid loops) */ +/* limit for table tag-method chains (to avoid infinite loops) */ #define MAXTAGLOOP 100 -- 2.54.0 From 864e28fcf77fd5d16aa2c6ca7ea09a85cfa77402 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 18 Feb 2022 13:22:25 -0300 Subject: [PATCH 13/17] Avoid computing invalid addresses luaV_execute should compute 'ra' only when the instruction uses it. Computing an illegal address is undefined behavior even if the address is never dereferenced. --- src/blua/lvm.c | 54 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 0d54a1757..b7015e0a2 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -399,6 +399,7 @@ static void Arith (lua_State *L, StkId ra, TValue *rb, #define arith_op(op,tm) { \ + StkId ra = RA(i); \ TValue *rb = RKB(i); \ TValue *rc = RKC(i); \ if (ttisnumber(rb) && ttisnumber(rc)) { \ @@ -443,7 +444,6 @@ void luaV_execute (lua_State *L, int nexeccalls) { /* main loop of interpreter */ for (;;) { const Instruction i = *pc++; - StkId ra; if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { traceexec(L, pc); @@ -453,26 +453,28 @@ void luaV_execute (lua_State *L, int nexeccalls) { } base = L->base; } - /* warning!! several calls may realloc the stack and invalidate `ra' */ - ra = RA(i); lua_assert(base == L->base && L->base == L->ci->base); lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); switch (GET_OPCODE(i)) { case OP_MOVE: { + StkId ra = RA(i); setobjs2s(L, ra, RB(i)); continue; } case OP_LOADK: { + StkId ra = RA(i); setobj2s(L, ra, KBx(i)); continue; } case OP_LOADBOOL: { + StkId ra = RA(i); setbvalue(ra, GETARG_B(i)); if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ continue; } case OP_LOADNIL: { + StkId ra = RA(i); TValue *rb = RB(i); do { setnilvalue(rb--); @@ -480,11 +482,13 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_GETUPVAL: { + StkId ra = RA(i); int b = GETARG_B(i); setobj2s(L, ra, cl->upvals[b]->v); continue; } case OP_GETGLOBAL: { + StkId ra = RA(i); TValue g; TValue *rb = KBx(i); sethvalue(L, &g, cl->env); @@ -493,12 +497,14 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_GETTABLE: { + StkId ra = RA(i); StkId rb = RB(i); TValue *rc = RKC(i); gettableProtected(L, rb, rc, ra); continue; } case OP_SETGLOBAL: { + StkId ra = RA(i); TValue g; sethvalue(L, &g, cl->env); lua_assert(ttisstring(KBx(i))); @@ -506,18 +512,21 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SETUPVAL: { + StkId ra = RA(i); UpVal *uv = cl->upvals[GETARG_B(i)]; setobj(L, uv->v, ra); luaC_barrier(L, uv, ra); continue; } case OP_SETTABLE: { + StkId ra = RA(i); TValue *rb = RKB(i); TValue *rc = RKC(i); settableProtected(L, ra, rb, rc); continue; } case OP_NEWTABLE: { + StkId ra = RA(i); int b = GETARG_B(i); int c = GETARG_C(i); sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); @@ -525,6 +534,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SELF: { + StkId ra = RA(i); const TValue *slot; StkId rb = RB(i); TValue *rc = RKC(i); @@ -549,6 +559,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_DIV: { + StkId ra = RA(i); TValue *rb = RKB(i); TValue *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { @@ -564,6 +575,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_MOD: { + StkId ra = RA(i); TValue *rb = RKB(i); TValue *rc = RKC(i); if (ttisnumber(rb) && ttisnumber(rc)) { @@ -583,26 +595,27 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_BAND: { - arith_op(luai_numand, TM_AND); - continue; + arith_op(luai_numand, TM_AND); + continue; } case OP_BOR: { - arith_op(luai_numor, TM_OR); - continue; + arith_op(luai_numor, TM_OR); + continue; } case OP_BXOR: { - arith_op(luai_numxor, TM_XOR); - continue; + arith_op(luai_numxor, TM_XOR); + continue; } case OP_BSHL: { - arith_op(luai_numshl, TM_SHL); - continue; + arith_op(luai_numshl, TM_SHL); + continue; } case OP_BSHR: { - arith_op(luai_numshr, TM_SHR); - continue; + arith_op(luai_numshr, TM_SHR); + continue; } case OP_BNOT: { + StkId ra = RA(i); TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); @@ -614,6 +627,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_UNM: { + StkId ra = RA(i); TValue *rb = RB(i); if (ttisnumber(rb)) { lua_Number nb = nvalue(rb); @@ -625,11 +639,13 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_NOT: { + StkId ra = RA(i); int res = l_isfalse(RB(i)); /* next assignment may change this value */ setbvalue(ra, res); continue; } case OP_LEN: { + StkId ra = RA(i); TValue *rb = RB(i); switch (ttype(rb)) { case LUA_TTABLE: { @@ -687,12 +703,14 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_TEST: { + StkId ra = RA(i); if (l_isfalse(ra) != GETARG_C(i)) dojump(L, pc, GETARG_sBx(*pc)); pc++; continue; } case OP_TESTSET: { + StkId ra = RA(i); TValue *rb = RB(i); if (l_isfalse(rb) != GETARG_C(i)) { setobjs2s(L, ra, rb); @@ -702,6 +720,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_CALL: { + StkId ra = RA(i); int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ @@ -723,6 +742,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { } } case OP_TAILCALL: { + StkId ra = RA(i); int b = GETARG_B(i); if (b != 0) L->top = ra+b; /* else previous instruction set top */ L->savedpc = pc; @@ -755,6 +775,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { } } case OP_RETURN: { + StkId ra = RA(i); int b = GETARG_B(i); if (b != 0) L->top = ra+b-1; if (L->openupval) luaF_close(L, base); @@ -770,6 +791,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { } } case OP_FORLOOP: { + StkId ra = RA(i); lua_Number step = nvalue(ra+2); lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ lua_Number limit = nvalue(ra+1); @@ -782,6 +804,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_FORPREP: { + StkId ra = RA(i); const TValue *init = ra; const TValue *plimit = ra+1; const TValue *pstep = ra+2; @@ -798,6 +821,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_TFORLOOP: { + StkId ra = RA(i); StkId cb = ra + 3; /* call base */ setobjs2s(L, cb+2, ra+2); setobjs2s(L, cb+1, ra+1); @@ -814,6 +838,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SETLIST: { + StkId ra = RA(i); int n = GETARG_B(i); int c = GETARG_C(i); int last; @@ -836,10 +861,12 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_CLOSE: { + StkId ra = RA(i); luaF_close(L, ra); continue; } case OP_CLOSURE: { + StkId ra = RA(i); Proto *p; Closure *ncl; int nup, j; @@ -860,6 +887,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_VARARG: { + StkId ra = RA(i); int b = GETARG_B(i) - 1; int j; CallInfo *ci = L->ci; -- 2.54.0 From 91de6336337b1d26de00bd90eca594dcc11fc010 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 11 Feb 2026 17:02:20 +0100 Subject: [PATCH 14/17] =?UTF-8?q?Experiment:=20replace=20lua=C2=B4s=20stri?= =?UTF-8?q?ng=20hash=20with=20luajits=20ARX=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit the string hashing was the most hit and most spent in part of the whole game in alot of cases replacing it with luajits algo absolutely massively improves lua performance (10-15fps in my bench timedemos!) however idk how all this stuff exactly works so this needs alot of testing to make sure nothing breaks ive rewritten the unaligned ptr access slightly to use memcpy for safety with some platforms that require stricter alignment --- src/blua/lstring.c | 54 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/src/blua/lstring.c b/src/blua/lstring.c index 44086dd28..a826ae32f 100644 --- a/src/blua/lstring.c +++ b/src/blua/lstring.c @@ -72,13 +72,63 @@ static TString *newlstr (lua_State *L, const char *str, size_t l, } -TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { - GCObject *o; +// string hash taken from luaJIT + +/* Unaligned load of uint32_t. */ +static inline __attribute__((always_inline)) uint32_t getu32(const void* ptr) +{ + uint32_t result; + memcpy(&result, ptr, 4); + return result; +} + +// smol macro to look a little less messy lul +#define getu8(p) *cast(const uint8_t *, p) + +/* Keyed sparse ARX string hash. Constant time. */ +static unsigned hash_sparse(const char *str, size_t len) +{ + /* Constants taken from lookup3 hash by Bob Jenkins. */ + unsigned int a, b, h = cast(unsigned int, len); +#define rol(x, n) (((x)<<(n)) | ((x)>>(-cast(int, n)&(8*sizeof(x)-1)))) + if (len >= 4) { /* Caveat: unaligned access! */ + a = getu32(str); + h ^= getu32(str+len-4); + b = getu32(str+(len>>1)-2); + h ^= b; h -= rol(b, 14); + b += getu32(str+(len>>2)-1); + } else { + a = getu8(str); + h ^= getu8(str+len-1); + b = getu8(str+(len>>1)); + h ^= b; h -= rol(b, 14); + } + a ^= h; a -= rol(h, 11); + b ^= a; b -= rol(a, 25); + h ^= b; h -= rol(b, 16); +#undef rol + return h; +} + +#undef getu8 + + +static inline __attribute__((always_inline)) unsigned luaS_hash (const char *str, size_t l) { +#if 0 unsigned int h = cast(unsigned int, l); /* seed */ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ size_t l1; for (l1=l; l1>=step; l1-=step) /* compute hash */ h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + return h; +#else + return hash_sparse(str, l); +#endif +} + +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + GCObject *o; + unsigned int h = luaS_hash(str, l); for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; o != NULL; o = o->gch.next) { -- 2.54.0 From 44e377c9815e83d02aba2423e7da142d06c85cc9 Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 21 Feb 2026 23:12:44 +0100 Subject: [PATCH 15/17] blua: handle 0 length strings in hash_sparse --- src/blua/lstring.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/blua/lstring.c b/src/blua/lstring.c index a826ae32f..b2d7306e5 100644 --- a/src/blua/lstring.c +++ b/src/blua/lstring.c @@ -90,6 +90,9 @@ static unsigned hash_sparse(const char *str, size_t len) { /* Constants taken from lookup3 hash by Bob Jenkins. */ unsigned int a, b, h = cast(unsigned int, len); + + if (len == 0) return 0; + #define rol(x, n) (((x)<<(n)) | ((x)>>(-cast(int, n)&(8*sizeof(x)-1)))) if (len >= 4) { /* Caveat: unaligned access! */ a = getu32(str); -- 2.54.0 From 66bc684738f83db18788878838ab1c868d4c452d Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 22 Feb 2026 01:06:15 +0100 Subject: [PATCH 16/17] luaS_newlstr: use hash also for checking strings taken from raptorjit we can check the hash first to skip quite a bunch of memcmps which should be faster in most cases (hopefully but quick timedemo tests confirmed that) --- src/blua/lstring.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/blua/lstring.c b/src/blua/lstring.c index b2d7306e5..a84de4b08 100644 --- a/src/blua/lstring.c +++ b/src/blua/lstring.c @@ -90,9 +90,7 @@ static unsigned hash_sparse(const char *str, size_t len) { /* Constants taken from lookup3 hash by Bob Jenkins. */ unsigned int a, b, h = cast(unsigned int, len); - if (len == 0) return 0; - #define rol(x, n) (((x)<<(n)) | ((x)>>(-cast(int, n)&(8*sizeof(x)-1)))) if (len >= 4) { /* Caveat: unaligned access! */ a = getu32(str); @@ -136,7 +134,9 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { o != NULL; o = o->gch.next) { TString *ts = rawgco2ts(o); - if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { + if (ts->tsv.hash == h && + ts->tsv.len == l && + (memcmp(str, getstr(ts), l) == 0)) { /* string may be dead */ if (isdead(G(L), o)) changewhite(o); return ts; -- 2.54.0 From 0016d4836ae23094eb5d7746ccd36dd357ccfca1 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 22 Feb 2026 14:03:50 +0100 Subject: [PATCH 17/17] hash_sparse: change length checks dont early return on 0 len, let it be the falltrough case, this seems to help branch prediction --- src/blua/lstring.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/blua/lstring.c b/src/blua/lstring.c index a84de4b08..b1249238b 100644 --- a/src/blua/lstring.c +++ b/src/blua/lstring.c @@ -90,7 +90,6 @@ static unsigned hash_sparse(const char *str, size_t len) { /* Constants taken from lookup3 hash by Bob Jenkins. */ unsigned int a, b, h = cast(unsigned int, len); - if (len == 0) return 0; #define rol(x, n) (((x)<<(n)) | ((x)>>(-cast(int, n)&(8*sizeof(x)-1)))) if (len >= 4) { /* Caveat: unaligned access! */ a = getu32(str); @@ -98,11 +97,13 @@ static unsigned hash_sparse(const char *str, size_t len) b = getu32(str+(len>>1)-2); h ^= b; h -= rol(b, 14); b += getu32(str+(len>>2)-1); - } else { + } else if (len > 0) { a = getu8(str); h ^= getu8(str+len-1); b = getu8(str+(len>>1)); h ^= b; h -= rol(b, 14); + } else { + return 0; } a ^= h; a -= rol(h, 11); b ^= a; b -= rol(a, 25); -- 2.54.0