diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-06-27 11:24:27 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2024-06-27 11:24:27 -0300 |
| commit | 9904c253da9690728710082cfb94654709ab89e7 (patch) | |
| tree | f556ebbf8d99baa37ea8e39608a3d6bea77f8267 | |
| parent | fb7e5b76c9d41108c399cf4d16470018b717007b (diff) | |
| download | lua-9904c253da9690728710082cfb94654709ab89e7.tar.gz lua-9904c253da9690728710082cfb94654709ab89e7.tar.bz2 lua-9904c253da9690728710082cfb94654709ab89e7.zip | |
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.
| -rw-r--r-- | lcode.c | 2 | ||||
| -rw-r--r-- | lopcodes.h | 3 | ||||
| -rw-r--r-- | lparser.c | 21 | ||||
| -rw-r--r-- | ltests.c | 1 | ||||
| -rw-r--r-- | testes/code.lua | 11 |
5 files changed, 31 insertions, 7 deletions
| @@ -1804,7 +1804,7 @@ void luaK_settablesize (FuncState *fs, int pc, int ra, int asize, int hsize) { | |||
| 1804 | ** table (or LUA_MULTRET to add up to stack top). | 1804 | ** table (or LUA_MULTRET to add up to stack top). |
| 1805 | */ | 1805 | */ |
| 1806 | void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { | 1806 | void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { |
| 1807 | lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH); | 1807 | lua_assert(tostore != 0); |
| 1808 | if (tostore == LUA_MULTRET) | 1808 | if (tostore == LUA_MULTRET) |
| 1809 | tostore = 0; | 1809 | tostore = 0; |
| 1810 | if (nelems <= MAXARG_C) | 1810 | if (nelems <= MAXARG_C) |
| @@ -406,7 +406,4 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) | |||
| 406 | (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) | 406 | (((mm) << 7) | ((ot) << 6) | ((it) << 5) | ((t) << 4) | ((a) << 3) | (m)) |
| 407 | 407 | ||
| 408 | 408 | ||
| 409 | /* number of list items to accumulate before a SETLIST instruction */ | ||
| 410 | #define LFIELDS_PER_FLUSH 50 | ||
| 411 | |||
| 412 | #endif | 409 | #endif |
| @@ -843,13 +843,13 @@ static void yindex (LexState *ls, expdesc *v) { | |||
| 843 | ** ======================================================================= | 843 | ** ======================================================================= |
| 844 | */ | 844 | */ |
| 845 | 845 | ||
| 846 | |||
| 847 | typedef struct ConsControl { | 846 | typedef struct ConsControl { |
| 848 | expdesc v; /* last list item read */ | 847 | expdesc v; /* last list item read */ |
| 849 | expdesc *t; /* table descriptor */ | 848 | expdesc *t; /* table descriptor */ |
| 850 | int nh; /* total number of 'record' elements */ | 849 | int nh; /* total number of 'record' elements */ |
| 851 | int na; /* number of array elements already stored */ | 850 | int na; /* number of array elements already stored */ |
| 852 | int tostore; /* number of array elements pending to be stored */ | 851 | int tostore; /* number of array elements pending to be stored */ |
| 852 | int maxtostore; /* maximum number of pending elements */ | ||
| 853 | } ConsControl; | 853 | } ConsControl; |
| 854 | 854 | ||
| 855 | 855 | ||
| @@ -878,7 +878,7 @@ static void closelistfield (FuncState *fs, ConsControl *cc) { | |||
| 878 | if (cc->v.k == VVOID) return; /* there is no list item */ | 878 | if (cc->v.k == VVOID) return; /* there is no list item */ |
| 879 | luaK_exp2nextreg(fs, &cc->v); | 879 | luaK_exp2nextreg(fs, &cc->v); |
| 880 | cc->v.k = VVOID; | 880 | cc->v.k = VVOID; |
| 881 | if (cc->tostore == LFIELDS_PER_FLUSH) { | 881 | if (cc->tostore >= cc->maxtostore) { |
| 882 | luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ | 882 | luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ |
| 883 | cc->na += cc->tostore; | 883 | cc->na += cc->tostore; |
| 884 | cc->tostore = 0; /* no more items pending */ | 884 | cc->tostore = 0; /* no more items pending */ |
| @@ -931,6 +931,22 @@ static void field (LexState *ls, ConsControl *cc) { | |||
| 931 | } | 931 | } |
| 932 | 932 | ||
| 933 | 933 | ||
| 934 | /* | ||
| 935 | ** Compute a limit for how many registers a constructor can use before | ||
| 936 | ** emitting a 'SETLIST' instruction, based on how many registers are | ||
| 937 | ** available. | ||
| 938 | */ | ||
| 939 | static int maxtostore (FuncState *fs) { | ||
| 940 | int numfreeregs = MAX_FSTACK - fs->freereg; | ||
| 941 | if (numfreeregs >= 160) /* "lots" of registers? */ | ||
| 942 | return numfreeregs / 5u; /* use up to 1/5 of them */ | ||
| 943 | else if (numfreeregs >= 80) /* still "enough" registers? */ | ||
| 944 | return 10; /* one 'SETLIST' instruction for each 10 values */ | ||
| 945 | else /* save registers for potential more nesting */ | ||
| 946 | return 1; | ||
| 947 | } | ||
| 948 | |||
| 949 | |||
| 934 | static void constructor (LexState *ls, expdesc *t) { | 950 | static void constructor (LexState *ls, expdesc *t) { |
| 935 | /* constructor -> '{' [ field { sep field } [sep] ] '}' | 951 | /* constructor -> '{' [ field { sep field } [sep] ] '}' |
| 936 | sep -> ',' | ';' */ | 952 | sep -> ',' | ';' */ |
| @@ -945,6 +961,7 @@ static void constructor (LexState *ls, expdesc *t) { | |||
| 945 | luaK_reserveregs(fs, 1); | 961 | luaK_reserveregs(fs, 1); |
| 946 | init_exp(&cc.v, VVOID, 0); /* no value (yet) */ | 962 | init_exp(&cc.v, VVOID, 0); /* no value (yet) */ |
| 947 | checknext(ls, '{'); | 963 | checknext(ls, '{'); |
| 964 | cc.maxtostore = maxtostore(fs); | ||
| 948 | do { | 965 | do { |
| 949 | lua_assert(cc.v.k == VVOID || cc.tostore > 0); | 966 | lua_assert(cc.v.k == VVOID || cc.tostore > 0); |
| 950 | if (ls->t.token == '}') break; | 967 | if (ls->t.token == '}') break; |
| @@ -835,7 +835,6 @@ static int get_limits (lua_State *L) { | |||
| 835 | setnameval(L, "MAXARG_Ax", MAXARG_Ax); | 835 | setnameval(L, "MAXARG_Ax", MAXARG_Ax); |
| 836 | setnameval(L, "MAXARG_Bx", MAXARG_Bx); | 836 | setnameval(L, "MAXARG_Bx", MAXARG_Bx); |
| 837 | setnameval(L, "OFFSET_sBx", OFFSET_sBx); | 837 | setnameval(L, "OFFSET_sBx", OFFSET_sBx); |
| 838 | setnameval(L, "LFPF", LFIELDS_PER_FLUSH); | ||
| 839 | setnameval(L, "NUM_OPCODES", NUM_OPCODES); | 838 | setnameval(L, "NUM_OPCODES", NUM_OPCODES); |
| 840 | return 1; | 839 | return 1; |
| 841 | } | 840 | } |
diff --git a/testes/code.lua b/testes/code.lua index 329619f1..08b3e23f 100644 --- a/testes/code.lua +++ b/testes/code.lua | |||
| @@ -460,5 +460,16 @@ do -- check number of available registers | |||
| 460 | assert(string.find(msg, "too many registers")) | 460 | assert(string.find(msg, "too many registers")) |
| 461 | end | 461 | end |
| 462 | 462 | ||
| 463 | |||
| 464 | do -- basic check for SETLIST | ||
| 465 | -- create a list constructor with 50 elements | ||
| 466 | local source = "local a; return {" .. string.rep("a, ", 50) .. "}" | ||
| 467 | local func = assert(load(source)) | ||
| 468 | local code = table.concat(T.listcode(func), "\n") | ||
| 469 | local _, count = string.gsub(code, "SETLIST", "") | ||
| 470 | -- code uses only 1 SETLIST for the constructor | ||
| 471 | assert(count == 1) | ||
| 472 | end | ||
| 473 | |||
| 463 | print 'OK' | 474 | print 'OK' |
| 464 | 475 | ||
