From 1fb4d539254b67e7e35ed698250c66d1edff0e08 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 12 Jul 2019 16:13:50 -0300 Subject: [PATCH] OP_NEWTABLE keeps exact size of arrays OP_NEWTABLE is followed by an OP_EXTRAARG, so that it can keep the exact size of the array part of the table to be created. (Functions 'luaO_int2fb'/'luaO_fb2int' were removed.) --- lcode.c | 6 +++--- lcode.h | 1 + lobject.c | 26 ----------------------- lobject.h | 2 -- lopcodes.h | 10 ++++++++- lparser.c | 37 +++++++++++++++++++++++---------- ltests.c | 9 -------- lvm.c | 6 +++++- testes/code.lua | 6 ++++-- testes/nextvar.lua | 52 +++++++++++++++++----------------------------- 10 files changed, 67 insertions(+), 88 deletions(-) diff --git a/lcode.c b/lcode.c index 74ff47de..1ff32ed7 100644 --- a/lcode.c +++ b/lcode.c @@ -430,7 +430,7 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k) { /* ** Emit an "extra argument" instruction (format 'iAx') */ -static int codeextraarg (FuncState *fs, int a) { +int luaK_codeextraarg (FuncState *fs, int a) { lua_assert(a <= MAXARG_Ax); return luaK_code(fs, CREATE_Ax(OP_EXTRAARG, a)); } @@ -446,7 +446,7 @@ static int luaK_codek (FuncState *fs, int reg, int k) { return luaK_codeABx(fs, OP_LOADK, reg, k); else { int p = luaK_codeABx(fs, OP_LOADKX, reg, 0); - codeextraarg(fs, k); + luaK_codeextraarg(fs, k); return p; } } @@ -1687,7 +1687,7 @@ void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { luaK_codeABC(fs, OP_SETLIST, base, b, c); else if (c <= MAXARG_Ax) { luaK_codeABC(fs, OP_SETLIST, base, b, 0); - codeextraarg(fs, c); + luaK_codeextraarg(fs, c); } else luaX_syntaxerror(fs->ls, "constructor too long"); diff --git a/lcode.h b/lcode.h index a15b6875..a924722c 100644 --- a/lcode.h +++ b/lcode.h @@ -55,6 +55,7 @@ LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, int B, int C, int k); +LUAI_FUNC int luaK_codeextraarg (FuncState *fs, int a); LUAI_FUNC int luaK_isKint (expdesc *e); LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); LUAI_FUNC void luaK_fixline (FuncState *fs, int line); diff --git a/lobject.c b/lobject.c index 2c265f96..b4efae4f 100644 --- a/lobject.c +++ b/lobject.c @@ -29,32 +29,6 @@ #include "lvm.h" -/* -** converts an integer to a "floating point byte", represented as -** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if -** eeeee != 0 and (xxx) otherwise. -*/ -int luaO_int2fb (unsigned int x) { - int e = 0; /* exponent */ - if (x < 8) return x; - while (x >= (8 << 4)) { /* coarse steps */ - x = (x + 0xf) >> 4; /* x = ceil(x / 16) */ - e += 4; - } - while (x >= (8 << 1)) { /* fine steps */ - x = (x + 1) >> 1; /* x = ceil(x / 2) */ - e++; - } - return ((e+1) << 3) | (cast_int(x) - 8); -} - - -/* converts back */ -int luaO_fb2int (int x) { - return (x < 8) ? x : ((x & 7) + 8) << ((x >> 3) - 1); -} - - /* ** Computes ceil(log2(x)) */ diff --git a/lobject.h b/lobject.h index 2f95bcb5..95f8e188 100644 --- a/lobject.h +++ b/lobject.h @@ -734,8 +734,6 @@ typedef struct Table { /* size of buffer for 'luaO_utf8esc' function */ #define UTF8BUFFSZ 8 -LUAI_FUNC int luaO_int2fb (unsigned int x); -LUAI_FUNC int luaO_fb2int (int x); LUAI_FUNC int luaO_utf8esc (char *buff, unsigned long x); LUAI_FUNC int luaO_ceillog2 (unsigned int x); LUAI_FUNC int luaO_rawarith (lua_State *L, int op, const TValue *p1, diff --git a/lopcodes.h b/lopcodes.h index 7bbbb0e5..0b23fa6f 100644 --- a/lopcodes.h +++ b/lopcodes.h @@ -324,7 +324,8 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if (C == 0) then next 'instruction' is EXTRAARG(real C). - (*) In OP_LOADKX, the next 'instruction' is always EXTRAARG. + (*) In OP_LOADKX and OP_NEWTABLE, the next 'instruction' is always + EXTRAARG. (*) For comparisons, k specifies what condition the test should accept (true or false). @@ -375,4 +376,11 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];) #define LFIELDS_PER_FLUSH 50 +/* +** In OP_NEWTABLE, array sizes smaller than LIMTABSZ are represented +** directly in R(B). Otherwise, array size is given by +** (R(B) - LIMTABSZ) + EXTRAARG * LFIELDS_PER_FLUSH +*/ +#define LIMTABSZ (MAXARG_B - LFIELDS_PER_FLUSH) + #endif diff --git a/lparser.c b/lparser.c index 79df0217..193e50f1 100644 --- a/lparser.c +++ b/lparser.c @@ -811,16 +811,16 @@ static void yindex (LexState *ls, expdesc *v) { */ -struct ConsControl { +typedef struct ConsControl { expdesc v; /* last list item read */ expdesc *t; /* table descriptor */ int nh; /* total number of 'record' elements */ int na; /* total number of array elements */ int tostore; /* number of array elements pending to be stored */ -}; +} ConsControl; -static void recfield (LexState *ls, struct ConsControl *cc) { +static void recfield (LexState *ls, ConsControl *cc) { /* recfield -> (NAME | '['exp']') = exp */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; @@ -841,7 +841,7 @@ static void recfield (LexState *ls, struct ConsControl *cc) { } -static void closelistfield (FuncState *fs, struct ConsControl *cc) { +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; @@ -852,7 +852,7 @@ static void closelistfield (FuncState *fs, struct ConsControl *cc) { } -static void lastlistfield (FuncState *fs, struct ConsControl *cc) { +static void lastlistfield (FuncState *fs, ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); @@ -867,16 +867,15 @@ static void lastlistfield (FuncState *fs, struct ConsControl *cc) { } -static void listfield (LexState *ls, struct ConsControl *cc) { +static void listfield (LexState *ls, ConsControl *cc) { /* listfield -> exp */ expr(ls, &cc->v); - checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); cc->na++; cc->tostore++; } -static void field (LexState *ls, struct ConsControl *cc) { +static void field (LexState *ls, ConsControl *cc) { /* field -> listfield | recfield */ switch(ls->t.token) { case TK_NAME: { /* may be 'listfield' or 'recfield' */ @@ -898,13 +897,30 @@ static void field (LexState *ls, struct ConsControl *cc) { } +static void settablesize (FuncState *fs, ConsControl *cc, int pc) { + Instruction *inst = &fs->f->code[pc]; + int rc = (cc->nh == 0) ? 0 : luaO_ceillog2(cc->nh) + 1; + int rb = cc->na; + int extra = 0; + if (rb >= LIMTABSZ) { + extra = rb / LFIELDS_PER_FLUSH; + rb = rb % LFIELDS_PER_FLUSH + LIMTABSZ; + checklimit(fs, extra, MAXARG_Ax, "items in a constructor"); + } + SETARG_C(*inst, rc); /* set initial table size */ + SETARG_B(*inst, rb); /* set initial array size */ + SETARG_Ax(*(inst + 1), extra); +} + + static void constructor (LexState *ls, expdesc *t) { /* constructor -> '{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); - struct ConsControl cc; + ConsControl cc; + luaK_codeextraarg(fs, 0); cc.na = cc.nh = cc.tostore = 0; cc.t = t; init_exp(t, VRELOC, pc); @@ -919,8 +935,7 @@ static void constructor (LexState *ls, expdesc *t) { } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); - SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ - SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ + settablesize(fs, &cc, pc); } /* }====================================================================== */ diff --git a/ltests.c b/ltests.c index dc830657..cb8c422a 100644 --- a/ltests.c +++ b/ltests.c @@ -1103,14 +1103,6 @@ static int doremote (lua_State *L) { } -static int int2fb_aux (lua_State *L) { - int b = luaO_int2fb((unsigned int)luaL_checkinteger(L, 1)); - lua_pushinteger(L, b); - lua_pushinteger(L, (unsigned int)luaO_fb2int(b)); - return 2; -} - - static int log2_aux (lua_State *L) { unsigned int x = (unsigned int)luaL_checkinteger(L, 1); lua_pushinteger(L, luaO_ceillog2(x)); @@ -1780,7 +1772,6 @@ static const struct luaL_Reg tests_funcs[] = { {"pobj", gc_printobj}, {"getref", getref}, {"hash", hash_query}, - {"int2fb", int2fb_aux}, {"log2", log2_aux}, {"limits", get_limits}, {"listcode", listcode}, diff --git a/lvm.c b/lvm.c index a52f186f..4011819d 100644 --- a/lvm.c +++ b/lvm.c @@ -1250,11 +1250,15 @@ void luaV_execute (lua_State *L, CallInfo *ci) { int b = GETARG_B(i); int c = GETARG_C(i); Table *t; + c = (c == 0) ? 0 : 1 << (c - 1); /* size is 2^c */ + if (b >= LIMTABSZ) + b += LFIELDS_PER_FLUSH * GETARG_Ax(*pc) - LIMTABSZ; + pc++; /* skip extra argument */ L->top = ci->top; /* correct top in case of GC */ t = luaH_new(L); /* memory allocation */ sethvalue2s(L, ra, t); if (b != 0 || c != 0) - luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c)); /* idem */ + luaH_resize(L, t, b, c); /* idem */ checkGC(L, ra + 1); vmbreak; } diff --git a/testes/code.lua b/testes/code.lua index 49d682f8..b2702c61 100644 --- a/testes/code.lua +++ b/testes/code.lua @@ -93,11 +93,13 @@ end -- some basic instructions check(function () -- function does not create upvalues (function () end){f()} -end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN0') +end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL', + 'SETLIST', 'CALL', 'RETURN0') check(function (x) -- function creates upvalues (function () return x end){f()} -end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN') +end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL', + 'SETLIST', 'CALL', 'RETURN') -- sequence of LOADNILs diff --git a/testes/nextvar.lua b/testes/nextvar.lua index 87a6bfa8..bdc9fc29 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua @@ -49,33 +49,13 @@ if not T then else --[ -- testing table sizes -local function log2 (x) return math.log(x, 2) end local function mp2 (n) -- minimum power of 2 >= n - local mp = 2^math.ceil(log2(n)) + local mp = 2^math.ceil(math.log(n, 2)) assert(n == 0 or (mp/2 < n and n <= mp)) return mp end -local function fb (n) - local r, nn = T.int2fb(n) - assert(r < 256) - return nn -end - --- test fb function -for a = 1, 10000 do -- all numbers up to 10^4 - local n = fb(a) - assert(a <= n and n <= a*1.125) -end -local a = 1024 -- plus a few up to 2 ^30 -local lim = 2^30 -while a < lim do - local n = fb(a) - assert(a <= n and n <= a*1.125) - a = math.ceil(a*1.3) -end - local function check (t, na, nh) local a, h = T.querytab(t) @@ -95,24 +75,30 @@ end -- testing constructor sizes -local lim = 40 -local s = 'return {' -for i=1,lim do - s = s..i..',' - local s = s - for k=0,lim do - local t = load(s..'}', '')() - assert(#t == i) - check(t, fb(i), mp2(k)) - s = string.format('%sa%d=%d,', s, k, k) +local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17, + 30, 31, 32, 33, 34, 500, 1000} + +for _, sa in ipairs(sizes) do -- 'sa' is size of the array part + local arr = {"return {"} + -- array part + for i = 1, sa do arr[1 + i] = "1," end + for _, sh in ipairs(sizes) do -- 'sh' is size of the hash part + for j = 1, sh do -- hash part + arr[1 + sa + j] = string.format('k%x=%d,', j, j) + end + arr[1 + sa + sh + 1] = "}" + local prog = table.concat(arr) + local t = assert(load(prog))() + assert(#t == sa) + check(t, sa, mp2(sh)) end end -- tests with unknown number of elements local a = {} -for i=1,lim do a[i] = i end -- build auxiliary table -for k=0,lim do +for i=1,sizes[#sizes] do a[i] = i end -- build auxiliary table +for k in ipairs(sizes) do local a = {table.unpack(a,1,k)} assert(#a == k) check(a, k, 0)