From a7ebc7e74539713989ea0c35af0e9b0d8c14bc14 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:39:19 +0100 Subject: [PATCH 01/16] 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 91435fe1b2bf99a28ff18946cf191890230d0dfc Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:39:56 +0100 Subject: [PATCH 02/16] 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 edc10a35a1e41e1143d41e7b1f64f1e10baa6bfb Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:40:51 +0100 Subject: [PATCH 03/16] 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 90b5f9050adaa97bf2bc20637dc4f2f5fee62c1c Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 16:41:42 +0100 Subject: [PATCH 04/16] 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 43608810da4feed212444d33b6eca83a34a756d9 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 11 Feb 2026 13:44:18 +0100 Subject: [PATCH 05/16] 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 02d2265c1..00e345478 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 9afc018a45ef24927fa39da303353038cb122340 Mon Sep 17 00:00:00 2001 From: Alug Date: Fri, 13 Mar 2026 18:58:05 +0100 Subject: [PATCH 06/16] 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 | 62 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 00e345478..7afbbd572 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -366,6 +366,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)) { \ @@ -392,7 +393,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); @@ -402,26 +402,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--); @@ -429,11 +431,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); @@ -442,10 +446,14 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_GETTABLE: { - Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + StkId ra = RA(i); + StkId rb = RB(i); + TValue *rc = RKC(i); + Protect(luaV_gettable(L, rb, rc, ra)); continue; } case OP_SETGLOBAL: { + StkId ra = RA(i); TValue g; sethvalue(L, &g, cl->env); lua_assert(ttisstring(KBx(i))); @@ -453,16 +461,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: { - Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + StkId ra = RA(i); + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect(luaV_settable(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))); @@ -470,6 +483,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SELF: { + StkId ra = RA(i); StkId rb = RB(i); setobjs2s(L, ra+1, rb); Protect(luaV_gettable(L, rb, RKC(i), ra)); @@ -488,6 +502,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)) { @@ -503,6 +518,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)) { @@ -522,26 +538,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); @@ -553,6 +570,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); @@ -564,11 +582,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: { @@ -626,12 +646,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); @@ -641,6 +663,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 */ @@ -662,6 +685,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; @@ -694,6 +718,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); @@ -709,6 +734,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); @@ -721,6 +747,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; @@ -737,6 +764,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); @@ -753,6 +781,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; @@ -775,10 +804,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; @@ -799,6 +830,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 966f60f590ffcfb62793d2298c6759fbd6f44f5c Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 11 Feb 2026 17:02:20 +0100 Subject: [PATCH 07/16] =?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 f6ead71f0ca6da4ee92987740edce4b2450bcced Mon Sep 17 00:00:00 2001 From: Alug Date: Sat, 21 Feb 2026 23:12:44 +0100 Subject: [PATCH 08/16] 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 1a4255d9df4088ebf73686fd18fe56162e2c7567 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 22 Feb 2026 01:06:15 +0100 Subject: [PATCH 09/16] 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 fed7020da179658edb27d8969c25661fc6b103c2 Mon Sep 17 00:00:00 2001 From: Alug Date: Sun, 22 Feb 2026 14:03:50 +0100 Subject: [PATCH 10/16] 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 From e4e51f0f80fb7deb9213c72bb374d0a7e4c3f03b Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 18:05:51 +0100 Subject: [PATCH 11/16] 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/lvm.c | 39 ++++++++++++++++++++------------------- src/blua/lvm.h | 27 +++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index 6a33983fe..990e17b6e 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, TValue *t, const char *k) { + 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/lvm.c b/src/blua/lvm.c index 7afbbd572..64e670d2f 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -105,32 +105,33 @@ 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, TValue *t, TValue *key, StkId val, + 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) { int loop; for (loop = 0; loop < MAXTAGLOOP; loop++) { diff --git a/src/blua/lvm.h b/src/blua/lvm.h index f9226ee5d..2ffc80ace 100644 --- a/src/blua/lvm.h +++ b/src/blua/lvm.h @@ -22,12 +22,35 @@ (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) { 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_finishget (lua_State *L, TValue *t, TValue *key, + StkId val, TValue *tm); LUAI_FUNC void luaV_settable (lua_State *L, TValue *t, TValue *key, StkId val); LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); -- 2.54.0 From 84be9096b134e1f8d17b5f444ee4c516943f4beb Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:00:29 +0100 Subject: [PATCH 12/16] 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 | 36 +++++++++++++++++++++++++----------- src/blua/lvm.h | 30 ++++++++++++++++++++++++++++-- 3 files changed, 73 insertions(+), 22 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index 990e17b6e..420e4e20b 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, TValue *t, const char *k) { + TValue key; + 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 64e670d2f..484329825 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -132,31 +132,45 @@ void luaV_finishget (lua_State *L, TValue *t, TValue *key, StkId val, luaG_runerror(L, "gettable chain too long; possible loop"); } -void luaV_settable (lua_State *L, 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, TValue *t, TValue *key, + StkId val, 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? */ + 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 2ffc80ace..2f65098f0 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) { 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, TValue *t, TValue *key, StkId val, TValue *tm); -LUAI_FUNC void luaV_settable (lua_State *L, TValue *t, TValue *key, - StkId val); +LUAI_FUNC void luaV_finishset (lua_State *L, TValue *t, TValue *key, + StkId val, 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 fdccc8026061806cef9c6f5cc2a4d9f752b4d00d Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:12:59 +0100 Subject: [PATCH 13/16] Blua: 'Protect' in table operations is not needed in the fast track https://github.com/lua/lua/commit/df8b996bccd3830d6fe9d7ba80e8fccd6ac3cabf --- src/blua/lvm.c | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/blua/lvm.c b/src/blua/lvm.c index 484329825..7540bcdec 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -393,6 +393,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) { 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) { 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; @@ -402,9 +417,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++; @@ -464,7 +479,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { StkId ra = RA(i); StkId rb = RB(i); TValue *rc = RKC(i); - Protect(luaV_gettable(L, rb, rc, ra)); + gettableProtected(L, rb, rc, ra); continue; } case OP_SETGLOBAL: { @@ -486,7 +501,7 @@ void luaV_execute (lua_State *L, int nexeccalls) { StkId ra = RA(i); TValue *rb = RKB(i); TValue *rc = RKC(i); - Protect(luaV_settable(L, ra, rb, rc)); + settableProtected(L, ra, rb, rc); continue; } case OP_NEWTABLE: { @@ -500,8 +515,9 @@ void luaV_execute (lua_State *L, int nexeccalls) { case OP_SELF: { StkId ra = RA(i); 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 c418fe3ef8419e1c626b8f894d93d210e35b94ec Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:21:36 +0100 Subject: [PATCH 14/16] 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 | 25 +++++++++---------- 3 files changed, 61 insertions(+), 46 deletions(-) diff --git a/src/blua/lapi.c b/src/blua/lapi.c index 420e4e20b..bca24a90f 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, TValue *t, const char *k) { - TValue *aux; + 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, TValue *t, const char *k) { TValue key; - TValue *aux; + 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 7540bcdec..9db792fd6 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -106,51 +106,67 @@ 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, TValue *t, TValue *key, StkId val, - TValue *tm) { + TValue *slot) { int loop; /* counter to avoid infinite loops */ - lua_assert(tm != NULL || !ttistable(t)); + 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, TValue *t, TValue *key, - StkId val, TValue *oldval) { + StkId val, TValue *slot) { int loop; /* counter to avoid infinite loops */ for (loop = 0; loop < MAXTAGLOOP; loop++) { 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; } @@ -166,7 +182,7 @@ void luaV_finishset (lua_State *L, 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 */ } @@ -397,9 +413,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) { 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) { 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 2f65098f0..0c4c877f2 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) { 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) { TValue *slot; \ + if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \ + else luaV_finishget(L,t,k,v,slot); } /* -- 2.54.0 From 0fb752b7459673d8a3b38c917f9593602b03c4a0 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:23:45 +0100 Subject: [PATCH 15/16] 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 9db792fd6..4d617d62c 100644 --- a/src/blua/lvm.c +++ b/src/blua/lvm.c @@ -529,11 +529,16 @@ void luaV_execute (lua_State *L, int nexeccalls) { continue; } case OP_SELF: { + TValue *slot; StkId ra = RA(i); 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 7684e6aa0750744e61cee0a29b948209c140d717 Mon Sep 17 00:00:00 2001 From: Alug Date: Wed, 3 Dec 2025 19:28:57 +0100 Subject: [PATCH 16/16] 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