commit 9904c253da9690728710082cfb94654709ab89e7
parent fb7e5b76c9d41108c399cf4d16470018b717007b
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Thu, 27 Jun 2024 11:24:00 -0300
Flexible limit for use of registers by constructors
Instead of a fixed limit of 50 registers (which, in a bad worst case,
can limit the nesting of constructors to 5 levels), the compiler
computes an individual limit for each constructor based on how many
registers are available when it runs. This limit then controls the
frequency of SETLIST instructions.
Diffstat:
5 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/lcode.c b/lcode.c
@@ -1804,7 +1804,7 @@ void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) {
** table (or LUA_MULTRET to add up to stack top).
*/
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
- lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
+ lua_assert(tostore != 0);
if (tostore == LUA_MULTRET)
tostore = 0;
if (nelems <= MAXARG_C)
diff --git a/lopcodes.h b/lopcodes.h
@@ -406,7 +406,4 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
(((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m))
-/* number of list items to accumulate before a SETLIST instruction */
-#define LFIELDS_PER_FLUSH 50
-
#endif
diff --git a/lparser.c b/lparser.c
@@ -843,13 +843,13 @@ static void yindex (LexState *ls, expdesc *v) {
** =======================================================================
*/
-
typedef struct ConsControl {
expdesc v; /* last list item read */
expdesc *t; /* table descriptor */
int nh; /* total number of 'record' elements */
int na; /* number of array elements already stored */
int tostore; /* number of array elements pending to be stored */
+ int maxtostore; /* maximum number of pending elements */
} ConsControl;
@@ -878,7 +878,7 @@ static void closelistfield (FuncState *fs, ConsControl *cc) {
if (cc->v.k == VVOID) return; /* there is no list item */
luaK_exp2nextreg(fs, &cc->v);
cc->v.k = VVOID;
- if (cc->tostore == LFIELDS_PER_FLUSH) {
+ if (cc->tostore >= cc->maxtostore) {
luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */
cc->na += cc->tostore;
cc->tostore = 0; /* no more items pending */
@@ -931,6 +931,22 @@ static void field (LexState *ls, ConsControl *cc) {
}
+/*
+** Compute a limit for how many registers a constructor can use before
+** emitting a 'SETLIST' instruction, based on how many registers are
+** available.
+*/
+static int maxtostore (FuncState *fs) {
+ int numfreeregs = MAX_FSTACK - fs->freereg;
+ if (numfreeregs >= 160) /* "lots" of registers? */
+ return numfreeregs / 5u; /* use up to 1/5 of them */
+ else if (numfreeregs >= 80) /* still "enough" registers? */
+ return 10; /* one 'SETLIST' instruction for each 10 values */
+ else /* save registers for potential more nesting */
+ return 1;
+}
+
+
static void constructor (LexState *ls, expdesc *t) {
/* constructor -> '{' [ field { sep field } [sep] ] '}'
sep -> ',' | ';' */
@@ -945,6 +961,7 @@ static void constructor (LexState *ls, expdesc *t) {
luaK_reserveregs(fs, 1);
init_exp(&cc.v, VVOID, 0); /* no value (yet) */
checknext(ls, '{');
+ cc.maxtostore = maxtostore(fs);
do {
lua_assert(cc.v.k == VVOID || cc.tostore > 0);
if (ls->t.token == '}') break;
diff --git a/ltests.c b/ltests.c
@@ -835,7 +835,6 @@ static int get_limits (lua_State *L) {
setnameval(L, "MAXARG_Ax", MAXARG_Ax);
setnameval(L, "MAXARG_Bx", MAXARG_Bx);
setnameval(L, "OFFSET_sBx", OFFSET_sBx);
- setnameval(L, "LFPF", LFIELDS_PER_FLUSH);
setnameval(L, "NUM_OPCODES", NUM_OPCODES);
return 1;
}
diff --git a/testes/code.lua b/testes/code.lua
@@ -460,5 +460,16 @@ do -- check number of available registers
assert(string.find(msg, "too many registers"))
end
+
+do -- basic check for SETLIST
+ -- create a list constructor with 50 elements
+ local source = "local a; return {" .. string.rep("a, ", 50) .. "}"
+ local func = assert(load(source))
+ local code = table.concat(T.listcode(func), "\n")
+ local _, count = string.gsub(code, "SETLIST", "")
+ -- code uses only 1 SETLIST for the constructor
+ assert(count == 1)
+end
+
print 'OK'