Browse Source

Unification of size representation in OP_NEWTABLE and OP_SETLIST

Opcodes OP_NEWTABLE and OP_SETLIST use the same representation to
store the size of the array part of a table. This new representation
can go up to 2^33 (8 + 25 bits).
Roberto Ierusalimschy 6 years ago
parent
commit
758c1ef445
6 changed files with 80 additions and 72 deletions
  1. 28 12
      lcode.c
  2. 3 1
      lcode.h
  3. 10 13
      lopcodes.h
  4. 7 22
      lparser.c
  5. 14 14
      lvm.c
  6. 18 10
      testes/nextvar.lua

+ 28 - 12
lcode.c

@@ -372,7 +372,7 @@ static void removelastinstruction (FuncState *fs) {
 ** Emit instruction 'i', checking for array sizes and saving also its
 ** line information. Return 'i' position.
 */
-static int luaK_code (FuncState *fs, Instruction i) {
+int luaK_code (FuncState *fs, Instruction i) {
   Proto *f = fs->f;
   /* put new instruction in code array */
   luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction,
@@ -430,7 +430,7 @@ static int codesJ (FuncState *fs, OpCode o, int sj, int k) {
 /*
 ** Emit an "extra argument" instruction (format 'iAx')
 */
-int luaK_codeextraarg (FuncState *fs, int a) {
+static int 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);
-    luaK_codeextraarg(fs, k);
+    codeextraarg(fs, k);
     return p;
   }
 }
@@ -1672,6 +1672,22 @@ void luaK_fixline (FuncState *fs, int line) {
 }
 
 
+void luaK_settablesize (FuncState *fs, int pc, int ra, int rc, int rb) {
+  Instruction *inst = &fs->f->code[pc];
+  int extra = 0;
+  int k = 0;
+  if (rb != 0)
+    rb = luaO_ceillog2(rb) + 1;  /* hash size */
+  if (rc > MAXARG_C) {  /* does it need the extra argument? */
+    extra = rc / (MAXARG_C + 1);
+    rc %= (MAXARG_C + 1);
+    k = 1;
+  }
+  *inst = CREATE_ABCk(OP_NEWTABLE, ra, rb, rc, k);
+  *(inst + 1) = CREATE_Ax(OP_EXTRAARG, extra);
+}
+
+
 /*
 ** Emit a SETLIST instruction.
 ** 'base' is register that keeps table;
@@ -1680,17 +1696,17 @@ void luaK_fixline (FuncState *fs, int line) {
 ** table (or LUA_MULTRET to add up to stack top).
 */
 void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
-  int c =  (nelems - 1)/LFIELDS_PER_FLUSH + 1;
-  int b = (tostore == LUA_MULTRET) ? 0 : tostore;
   lua_assert(tostore != 0 && tostore <= LFIELDS_PER_FLUSH);
-  if (c <= MAXARG_C)
-    luaK_codeABC(fs, OP_SETLIST, base, b, c);
-  else if (c <= MAXARG_Ax) {
-    luaK_codeABC(fs, OP_SETLIST, base, b, 0);
-    luaK_codeextraarg(fs, c);
+  if (tostore == LUA_MULTRET)
+    tostore = 0;
+  if (nelems <= MAXARG_C)
+    luaK_codeABC(fs, OP_SETLIST, base, tostore, nelems);
+  else {
+    int extra = nelems / (MAXARG_C + 1);
+    nelems %= (MAXARG_C + 1);
+    luaK_codeABCk(fs, OP_SETLIST, base, tostore, nelems, 1);
+    codeextraarg(fs, extra);
   }
-  else
-    luaX_syntaxerror(fs->ls, "constructor too long");
   fs->freereg = base + 1;  /* free registers with list values */
 }
 

+ 3 - 1
lcode.h

@@ -51,11 +51,11 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
 
 #define luaK_jumpto(fs,t)	luaK_patchlist(fs, luaK_jump(fs), t)
 
+LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
 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);
@@ -87,6 +87,8 @@ LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
 LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
 LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1,
                             expdesc *v2, int line);
+LUAI_FUNC void luaK_settablesize (FuncState *fs, int pc,
+                                                 int ra, int rb, int rc);
 LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
 LUAI_FUNC void luaK_finish (FuncState *fs);
 LUAI_FUNC l_noret luaK_semerror (LexState *ls, const char *msg);

+ 10 - 13
lopcodes.h

@@ -214,7 +214,7 @@ OP_SETTABLE,/*	A B C	R(A)[R(B)] := RK(C)				*/
 OP_SETI,/*	A B C	R(A)[B] := RK(C)				*/
 OP_SETFIELD,/*	A B C	R(A)[K(B):string] := RK(C)			*/
 
-OP_NEWTABLE,/*	A B C	R(A) := {} (size = B,C)				*/
+OP_NEWTABLE,/*	A B C	R(A) := {}					*/
 
 OP_SELF,/*	A B C	R(A+1) := R(B); R(A) := R(B)[RK(C):string]	*/
 
@@ -321,12 +321,17 @@ OP_EXTRAARG/*	Ax	extra (larger) argument for previous opcode	*/
 
   (*) In OP_RETURN, if (B == 0) then return up to 'top'.
 
-  (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if (C == 0) then
-  next 'instruction' is EXTRAARG(real C).
-
-  (*) In OP_LOADKX and OP_NEWTABLE, the next 'instruction' is always
+  (*) In OP_LOADKX and OP_NEWTABLE, the next instruction is always
   EXTRAARG.
 
+  (*) In OP_SETLIST, if (B == 0) then real B = 'top'; if k, then
+  real C = EXTRAARG _ C (the bits of EXTRAARG concatenated with the
+  bits of C).
+
+  (*) In OP_NEWTABLE, B is log2 of the hash size (which is always a
+  power of 2) plus 1, or zero for size zero. If not k, the array size
+  is C. Otherwise, the array size is EXTRAARG _ C.
+
   (*) For comparisons, k specifies what condition the test should accept
   (true or false).
 
@@ -375,12 +380,4 @@ LUAI_DDEC(const lu_byte luaP_opmodes[NUM_OPCODES];)
 /* number of list items to accumulate before a SETLIST instruction */
 #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

+ 7 - 22
lparser.c

@@ -815,7 +815,7 @@ 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 na;  /* number of array elements already stored */
   int tostore;  /* number of array elements pending to be stored */
 } ConsControl;
 
@@ -847,6 +847,7 @@ static void closelistfield (FuncState *fs, ConsControl *cc) {
   cc->v.k = VVOID;
   if (cc->tostore == LFIELDS_PER_FLUSH) {
     luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);  /* flush */
+    cc->na += cc->tostore;
     cc->tostore = 0;  /* no more items pending */
   }
 }
@@ -864,13 +865,13 @@ static void lastlistfield (FuncState *fs, ConsControl *cc) {
       luaK_exp2nextreg(fs, &cc->v);
     luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore);
   }
+  cc->na += cc->tostore;
 }
 
 
 static void listfield (LexState *ls, ConsControl *cc) {
   /* listfield -> exp */
   expr(ls, &cc->v);
-  cc->na++;
   cc->tostore++;
 }
 
@@ -897,22 +898,6 @@ static void field (LexState *ls, 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 -> ',' | ';' */
@@ -920,12 +905,12 @@ static void constructor (LexState *ls, expdesc *t) {
   int line = ls->linenumber;
   int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);
   ConsControl cc;
-  luaK_codeextraarg(fs, 0);
+  luaK_code(fs, 0);  /* space for extra arg. */
   cc.na = cc.nh = cc.tostore = 0;
   cc.t = t;
-  init_exp(t, VRELOC, pc);
+  init_exp(t, VNONRELOC, fs->freereg);  /* table will be at stack top */
+  luaK_reserveregs(fs, 1);
   init_exp(&cc.v, VVOID, 0);  /* no value (yet) */
-  luaK_exp2nextreg(ls->fs, t);  /* fix it at stack top */
   checknext(ls, '{');
   do {
     lua_assert(cc.v.k == VVOID || cc.tostore > 0);
@@ -935,7 +920,7 @@ static void constructor (LexState *ls, expdesc *t) {
   } while (testnext(ls, ',') || testnext(ls, ';'));
   check_match(ls, '}', '{', line);
   lastlistfield(fs, &cc);
-  settablesize(fs, &cc, pc);
+  luaK_settablesize(fs, pc, t->u.info, cc.na, cc.nh);
 }
 
 /* }====================================================================== */

+ 14 - 14
lvm.c

@@ -1247,18 +1247,19 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
         vmbreak;
       }
       vmcase(OP_NEWTABLE) {
-        int b = GETARG_B(i);
-        int c = GETARG_C(i);
+        int b = GETARG_B(i);  /* log2(hash size) + 1 */
+        int c = GETARG_C(i);  /* array size */
         Table *t;
-        c = (c == 0) ? 0 : 1 << (c - 1);  /* size is 2^c */
-        if (b >= LIMTABSZ)
-          b += LFIELDS_PER_FLUSH * GETARG_Ax(*pc) - LIMTABSZ;
+        if (b > 0)
+          b = 1 << (b - 1);  /* size is 2^(b - 1) */
+        if (TESTARG_k(i))
+          c += GETARG_Ax(*pc) * (MAXARG_C + 1);
         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, b, c);  /* idem */
+          luaH_resize(L, t, c, b);  /* idem */
         checkGC(L, ra + 1);
         vmbreak;
       }
@@ -1763,18 +1764,17 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
       }
       vmcase(OP_SETLIST) {
         int n = GETARG_B(i);
-        int c = GETARG_C(i);
-        unsigned int last;
-        Table *h;
+        unsigned int last = GETARG_C(i);
+        Table *h = hvalue(s2v(ra));
         if (n == 0)
-          n = cast_int(L->top - ra) - 1;
+          n = cast_int(L->top - ra) - 1;  /* get up to the top */
         else
           L->top = ci->top;  /* correct top in case of GC */
-        if (c == 0) {
-          c = GETARG_Ax(*pc); pc++;
+        last += n;
+        if (TESTARG_k(i)) {
+          last += GETARG_Ax(*pc) * (MAXARG_C + 1);
+          pc++;
         }
-        h = hvalue(s2v(ra));
-        last = ((c-1)*LFIELDS_PER_FLUSH) + n;
         if (last > luaH_realasize(h))  /* needs more space? */
           luaH_resizearray(L, h, last);  /* preallocate it at once */
         for (; n > 0; n--) {

+ 18 - 10
testes/nextvar.lua

@@ -80,15 +80,23 @@ local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
 
 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 i = 1, sa do arr[1 + i] = "1," end    -- build array part
   for _, sh in ipairs(sizes) do    -- 'sh' is size of the hash part
-    for j = 1, sh do   -- hash part
+    for j = 1, sh do   -- build 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))()
+    local f = assert(load(prog))
+    f()    -- call once to ensure stack space
+    -- make sure table is not resized after being created
+    if sa == 0 or sh == 0 then
+      T.alloccount(2);  -- header + array or hash part
+    else
+      T.alloccount(3);  -- header + array part + hash part
+    end
+    local t = f()
+    T.alloccount();
     assert(#t == sa)
     check(t, sa, mp2(sh))
   end
@@ -99,12 +107,12 @@ end
 local a = {}
 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)
-  a = {1,2,3,table.unpack(a,1,k)}
-  check(a, k+3, 0)
-  assert(#a == k + 3)
+  local t = {table.unpack(a,1,k)}
+  assert(#t == k)
+  check(t, k, 0)
+  t = {1,2,3,table.unpack(a,1,k)}
+  check(t, k+3, 0)
+  assert(#t == k + 3)
 end