Browse Source

jumps do not close upvalues (to be faster and simpler);
explicit instruction to close upvalues; command 'break' not
handled like a 'goto' (to optimize removal of uneeded 'close'
instructions)

Roberto Ierusalimschy 7 years ago
parent
commit
80d9b09f35
6 changed files with 139 additions and 67 deletions
  1. 41 10
      lcode.c
  2. 4 2
      lcode.h
  3. 4 2
      lopcodes.c
  4. 3 2
      lopcodes.h
  5. 81 46
      lparser.c
  6. 6 5
      lvm.c

+ 41 - 10
lcode.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lcode.c,v 2.120 2017/06/27 11:35:31 roberto Exp roberto $
+** $Id: lcode.c,v 2.121 2017/06/29 15:06:44 roberto Exp roberto $
 ** Code generator for Lua
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -272,20 +272,51 @@ void luaK_patchlist (FuncState *fs, int list, int target) {
 
 
 
 
 /*
 /*
-** Path all jumps in 'list' to close upvalues up to given 'level'
-** (The assertion checks that jumps either were closing nothing
-** or were closing higher levels, from inner blocks.)
+** Check whether some jump in given list needs a close instruction.
 */
 */
-void luaK_patchclose (FuncState *fs, int list, int level) {
-  level++;  /* argument is +1 to reserve 0 as non-op */
+int luaK_needclose (FuncState *fs, int list) {
   for (; list != NO_JUMP; list = getjump(fs, list)) {
   for (; list != NO_JUMP; list = getjump(fs, list)) {
-    lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP &&
-                (GETARG_A(fs->f->code[list]) == 0 ||
-                 GETARG_A(fs->f->code[list]) >= level));
-    SETARG_A(fs->f->code[list], level);
+    if (GETARG_A(fs->f->code[list]))  /* needs close? */
+      return 1;
+  }
+  return 0;
+}
+
+
+/*
+** Correct a jump list to jump to 'target'. If 'hasclose' is true,
+** 'target' contains an OP_CLOSE instruction (see first assert).
+** Only jumps with the A arg true need that close; other jumps
+** avoid it jumping to the next instruction.
+*/
+void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose) {
+  lua_assert(!hasclose || GET_OPCODE(fs->f->code[target]) == OP_CLOSE);
+  while (list != NO_JUMP) {
+    int next = getjump(fs, list);
+    lua_assert(!GETARG_A(fs->f->code[list]) || hasclose);
+    patchtestreg(fs, list, NO_REG);  /* do not generate values */
+    if (!hasclose || GETARG_A(fs->f->code[list]))
+      fixjump(fs, list, target);
+    else  /* there is a CLOSE instruction but jump does not need it */
+      fixjump(fs, list, target + 1);  /* avoid CLOSE instruction */
+    list = next;
   }
   }
 }
 }
 
 
+
+/*
+** Mark (using the A arg) all jumps in 'list' to close upvalues. Mark
+** will instruct 'luaK_patchgoto' to make these jumps go to OP_CLOSE
+** instructions.
+*/
+void luaK_patchclose (FuncState *fs, int list) {
+  for (; list != NO_JUMP; list = getjump(fs, list)) {
+    lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP);
+    SETARG_A(fs->f->code[list], 1);
+  }
+}
+
+
 #if !defined(MAXIWTHABS)
 #if !defined(MAXIWTHABS)
 #define MAXIWTHABS	120
 #define MAXIWTHABS	120
 #endif
 #endif

+ 4 - 2
lcode.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lcode.h,v 1.64 2016/01/05 16:22:37 roberto Exp roberto $
+** $Id: lcode.h,v 1.65 2017/04/20 19:53:55 roberto Exp roberto $
 ** Code generator for Lua
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -73,8 +73,10 @@ LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
 LUAI_FUNC int luaK_jump (FuncState *fs);
 LUAI_FUNC int luaK_jump (FuncState *fs);
 LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
 LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
 LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
 LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
+void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose);
 LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
 LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
-LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level);
+LUAI_FUNC void luaK_patchclose (FuncState *fs, int list);
+LUAI_FUNC int luaK_needclose (FuncState *fs, int list);
 LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
 LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
 LUAI_FUNC int luaK_getlabel (FuncState *fs);
 LUAI_FUNC int luaK_getlabel (FuncState *fs);
 LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);
 LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line);

+ 4 - 2
lopcodes.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lopcodes.c,v 1.59 2017/06/29 15:38:41 roberto Exp roberto $
+** $Id: lopcodes.c,v 1.60 2017/08/14 18:33:14 roberto Exp roberto $
 ** Opcodes for Lua virtual machine
 ** Opcodes for Lua virtual machine
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -54,6 +54,7 @@ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = {
   "NOT",
   "NOT",
   "LEN",
   "LEN",
   "CONCAT",
   "CONCAT",
+  "CLOSE",
   "JMP",
   "JMP",
   "EQ",
   "EQ",
   "LT",
   "LT",
@@ -115,7 +116,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
  ,opmode(0, 1, OpArgR, OpArgN, iABC)		/* OP_NOT */
  ,opmode(0, 1, OpArgR, OpArgN, iABC)		/* OP_NOT */
  ,opmode(0, 1, OpArgR, OpArgN, iABC)		/* OP_LEN */
  ,opmode(0, 1, OpArgR, OpArgN, iABC)		/* OP_LEN */
  ,opmode(0, 1, OpArgR, OpArgR, iABC)		/* OP_CONCAT */
  ,opmode(0, 1, OpArgR, OpArgR, iABC)		/* OP_CONCAT */
- ,opmode(0, 0, OpArgR, OpArgN, iAsBx)		/* OP_JMP */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC)		/* OP_CLOSE */
+ ,opmode(0, 0, OpArgU, OpArgN, iAsBx)		/* OP_JMP */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_EQ */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_EQ */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_LT */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_LT */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_LE */
  ,opmode(1, 0, OpArgK, OpArgK, iABC)		/* OP_LE */

+ 3 - 2
lopcodes.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lopcodes.h,v 1.155 2017/06/29 15:38:41 roberto Exp roberto $
+** $Id: lopcodes.h,v 1.156 2017/08/14 18:33:14 roberto Exp roberto $
 ** Opcodes for Lua virtual machine
 ** Opcodes for Lua virtual machine
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -217,7 +217,8 @@ OP_LEN,/*	A B	R(A) := length of R(B)				*/
 
 
 OP_CONCAT,/*	A B C	R(A) := R(B).. ... ..R(C)			*/
 OP_CONCAT,/*	A B C	R(A) := R(B).. ... ..R(C)			*/
 
 
-OP_JMP,/*	A sBx	pc+=sBx; if (A) close all upvalues >= R(A - 1)	*/
+OP_CLOSE,/*	A	close all upvalues >= R(A)			*/
+OP_JMP,/*	sBx	pc+=sBx						*/
 OP_EQ,/*	A B C	if ((RK(B) == RK(C)) ~= A) then pc++		*/
 OP_EQ,/*	A B C	if ((RK(B) == RK(C)) ~= A) then pc++		*/
 OP_LT,/*	A B C	if ((RK(B) <  RK(C)) ~= A) then pc++		*/
 OP_LT,/*	A B C	if ((RK(B) <  RK(C)) ~= A) then pc++		*/
 OP_LE,/*	A B C	if ((RK(B) <= RK(C)) ~= A) then pc++		*/
 OP_LE,/*	A B C	if ((RK(B) <= RK(C)) ~= A) then pc++		*/

+ 81 - 46
lparser.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lparser.c,v 2.163 2017/08/12 13:12:21 roberto Exp roberto $
+** $Id: lparser.c,v 2.164 2017/08/14 18:33:14 roberto Exp roberto $
 ** Lua Parser
 ** Lua Parser
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -49,6 +49,8 @@ typedef struct BlockCnt {
   struct BlockCnt *previous;  /* chain */
   struct BlockCnt *previous;  /* chain */
   int firstlabel;  /* index of first label in this block */
   int firstlabel;  /* index of first label in this block */
   int firstgoto;  /* index of first pending goto in this block */
   int firstgoto;  /* index of first pending goto in this block */
+  int brks;  /* list of break jumps in this block */
+  lu_byte brkcls;  /* true if some 'break' needs to close upvalues */
   lu_byte nactvar;  /* # active locals outside the block */
   lu_byte nactvar;  /* # active locals outside the block */
   lu_byte upval;  /* true if some variable in the block is an upvalue */
   lu_byte upval;  /* true if some variable in the block is an upvalue */
   lu_byte isloop;  /* true if 'block' is a loop */
   lu_byte isloop;  /* true if 'block' is a loop */
@@ -351,7 +353,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) {
       getstr(gt->name), gt->line, getstr(vname));
       getstr(gt->name), gt->line, getstr(vname));
     semerror(ls, msg);
     semerror(ls, msg);
   }
   }
-  luaK_patchlist(fs, gt->pc, label->pc);
+  luaK_patchgoto(fs, gt->pc, label->pc, 1);
   /* remove goto from pending list */
   /* remove goto from pending list */
   for (i = g; i < gl->n - 1; i++)
   for (i = g; i < gl->n - 1; i++)
     gl->arr[i] = gl->arr[i + 1];
     gl->arr[i] = gl->arr[i + 1];
@@ -362,7 +364,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) {
 /*
 /*
 ** try to close a goto with existing labels; this solves backward jumps
 ** try to close a goto with existing labels; this solves backward jumps
 */
 */
-static int findlabel (LexState *ls, int g) {
+static int solvelabel (LexState *ls, int g) {
   int i;
   int i;
   BlockCnt *bl = ls->fs->bl;
   BlockCnt *bl = ls->fs->bl;
   Dyndata *dyd = ls->dyd;
   Dyndata *dyd = ls->dyd;
@@ -373,7 +375,7 @@ static int findlabel (LexState *ls, int g) {
     if (eqstr(lb->name, gt->name)) {  /* correct label? */
     if (eqstr(lb->name, gt->name)) {  /* correct label? */
       if (gt->nactvar > lb->nactvar &&
       if (gt->nactvar > lb->nactvar &&
           (bl->upval || dyd->label.n > bl->firstlabel))
           (bl->upval || dyd->label.n > bl->firstlabel))
-        luaK_patchclose(ls->fs, gt->pc, lb->nactvar);
+        luaK_patchclose(ls->fs, gt->pc);
       closegoto(ls, g, lb);  /* close it */
       closegoto(ls, g, lb);  /* close it */
       return 1;
       return 1;
     }
     }
@@ -400,12 +402,12 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
 ** check whether new label 'lb' matches any pending gotos in current
 ** check whether new label 'lb' matches any pending gotos in current
 ** block; solves forward jumps
 ** block; solves forward jumps
 */
 */
-static void findgotos (LexState *ls, Labeldesc *lb) {
+static void solvegotos (LexState *ls, Labeldesc *lb) {
   Labellist *gl = &ls->dyd->gt;
   Labellist *gl = &ls->dyd->gt;
   int i = ls->fs->bl->firstgoto;
   int i = ls->fs->bl->firstgoto;
   while (i < gl->n) {
   while (i < gl->n) {
     if (eqstr(gl->arr[i].name, lb->name))
     if (eqstr(gl->arr[i].name, lb->name))
-      closegoto(ls, i, lb);
+      closegoto(ls, i, lb);  /* will remove 'i' from the list */
     else
     else
       i++;
       i++;
   }
   }
@@ -416,23 +418,32 @@ static void findgotos (LexState *ls, Labeldesc *lb) {
 ** export pending gotos to outer level, to check them against
 ** export pending gotos to outer level, to check them against
 ** outer labels; if the block being exited has upvalues, and
 ** outer labels; if the block being exited has upvalues, and
 ** the goto exits the scope of any variable (which can be the
 ** the goto exits the scope of any variable (which can be the
-** upvalue), close those variables being exited.
+** upvalue), close those variables being exited. Also export
+** break list.
 */
 */
 static void movegotosout (FuncState *fs, BlockCnt *bl) {
 static void movegotosout (FuncState *fs, BlockCnt *bl) {
   int i = bl->firstgoto;
   int i = bl->firstgoto;
   Labellist *gl = &fs->ls->dyd->gt;
   Labellist *gl = &fs->ls->dyd->gt;
   /* correct pending gotos to current block and try to close it
   /* correct pending gotos to current block and try to close it
      with visible labels */
      with visible labels */
-  while (i < gl->n) {
+  while (i < gl->n) {  /* for each pending goto */
     Labeldesc *gt = &gl->arr[i];
     Labeldesc *gt = &gl->arr[i];
-    if (gt->nactvar > bl->nactvar) {
-      if (bl->upval)
-        luaK_patchclose(fs, gt->pc, bl->nactvar);
-      gt->nactvar = bl->nactvar;
+    if (gt->nactvar > bl->nactvar) {  /* leaving a variable scope? */
+      if (bl->upval)  /* variable may be an upvalue? */
+        luaK_patchclose(fs, gt->pc);  /* jump will need a close */
+      gt->nactvar = bl->nactvar;  /* update goto level */
     }
     }
-    if (!findlabel(fs->ls, i))
+    if (!solvelabel(fs->ls, i))
       i++;  /* move to next one */
       i++;  /* move to next one */
+    /* else, 'solvelabel' removed current goto from the list
+       and 'i' now points to next one */
   }
   }
+  /* handles break list */
+  if (bl->upval)  /* exiting the scope of an upvalue? */
+    luaK_patchclose(fs, bl->brks);  /* breaks will need OP_CLOSE */
+  /* move breaks to outer block */
+  luaK_concat(fs, &bl->previous->brks, bl->brks);
+  bl->previous->brkcls |= bl->brkcls;
 }
 }
 
 
 
 
@@ -441,6 +452,8 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
   bl->nactvar = fs->nactvar;
   bl->nactvar = fs->nactvar;
   bl->firstlabel = fs->ls->dyd->label.n;
   bl->firstlabel = fs->ls->dyd->label.n;
   bl->firstgoto = fs->ls->dyd->gt.n;
   bl->firstgoto = fs->ls->dyd->gt.n;
+  bl->brks = NO_JUMP;
+  bl->brkcls = 0;
   bl->upval = 0;
   bl->upval = 0;
   bl->previous = fs->bl;
   bl->previous = fs->bl;
   fs->bl = bl;
   fs->bl = bl;
@@ -449,22 +462,24 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
 
 
 
 
 /*
 /*
-** create a label named 'break' to resolve break statements
+** Fix all breaks in block 'bl' to jump to the end of the block.
 */
 */
-static void breaklabel (LexState *ls) {
-  TString *n = luaS_new(ls->L, "break");
-  int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc);
-  findgotos(ls, &ls->dyd->label.arr[l]);
+static void fixbreaks (FuncState *fs, BlockCnt *bl) {
+  int target = fs->pc;
+  if (bl->brkcls)  /* does the block need to close upvalues? */
+    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+  luaK_patchgoto(fs, bl->brks, target, bl->brkcls);
+  bl->brks = NO_JUMP;  /* no more breaks to fix */
+  bl->brkcls = 0;  /* no more need to close upvalues */
+  lua_assert(!bl->upval);  /* loop body cannot have local variables */
 }
 }
 
 
+
 /*
 /*
-** generates an error for an undefined 'goto'; choose appropriate
-** message when label name is a reserved word (which can only be 'break')
+** generates an error for an undefined 'goto'.
 */
 */
 static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
 static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
-  const char *msg = isreserved(gt->name)
-                    ? "<%s> at line %d not inside a loop"
-                    : "no visible label '%s' for <goto> at line %d";
+  const char *msg = "no visible label '%s' for <goto> at line %d";
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
   semerror(ls, msg);
   semerror(ls, msg);
 }
 }
@@ -473,14 +488,12 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
 static void leaveblock (FuncState *fs) {
 static void leaveblock (FuncState *fs) {
   BlockCnt *bl = fs->bl;
   BlockCnt *bl = fs->bl;
   LexState *ls = fs->ls;
   LexState *ls = fs->ls;
-  if (bl->previous && bl->upval) {
-    /* create a 'jump to here' to close upvalues */
-    int j = luaK_jump(fs);
-    luaK_patchclose(fs, j, bl->nactvar);
-    luaK_patchtohere(fs, j);
-  }
+  if (bl->upval && bl->brks != NO_JUMP)  /* breaks in upvalue scopes? */
+    bl->brkcls = 1;  /* these breaks must close the upvalues */
   if (bl->isloop)
   if (bl->isloop)
-    breaklabel(ls);  /* close pending breaks */
+    fixbreaks(fs, bl);  /* fix pending breaks */
+  if (bl->previous && bl->upval)
+    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
   fs->bl = bl->previous;
   fs->bl = bl->previous;
   removevars(fs, bl->nactvar);
   removevars(fs, bl->nactvar);
   lua_assert(bl->nactvar == fs->nactvar);
   lua_assert(bl->nactvar == fs->nactvar);
@@ -488,8 +501,11 @@ static void leaveblock (FuncState *fs) {
   ls->dyd->label.n = bl->firstlabel;  /* remove local labels */
   ls->dyd->label.n = bl->firstlabel;  /* remove local labels */
   if (bl->previous)  /* inner block? */
   if (bl->previous)  /* inner block? */
     movegotosout(fs, bl);  /* update pending gotos to outer block */
     movegotosout(fs, bl);  /* update pending gotos to outer block */
-  else if (bl->firstgoto < ls->dyd->gt.n)  /* pending gotos in outer block? */
-    undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]);  /* error */
+  else {
+    lua_assert(bl->brks == NO_JUMP);  /* no pending breaks */
+    if (bl->firstgoto < ls->dyd->gt.n)  /* pending gotos in outer block? */
+      undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]);  /* error */
+  }
 }
 }
 
 
 
 
@@ -1205,16 +1221,21 @@ static int cond (LexState *ls) {
 
 
 static void gotostat (LexState *ls, int pc) {
 static void gotostat (LexState *ls, int pc) {
   int line = ls->linenumber;
   int line = ls->linenumber;
-  TString *label;
   int g;
   int g;
-  if (testnext(ls, TK_GOTO))
-    label = str_checkname(ls);
-  else {
-    luaX_next(ls);  /* skip break */
-    label = luaS_new(ls->L, "break");
-  }
-  g = newlabelentry(ls, &ls->dyd->gt, label, line, pc);
-  findlabel(ls, g);  /* close it if label already defined */
+  luaX_next(ls);  /* skip 'goto' */
+  g = newlabelentry(ls, &ls->dyd->gt, str_checkname(ls), line, pc);
+  solvelabel(ls, g);  /* close it if label already defined */
+}
+
+
+static void breakstat (LexState *ls, int pc) {
+  FuncState *fs = ls->fs;
+  BlockCnt *bl = fs->bl;
+  luaX_next(ls);  /* skip break */
+  while (bl && !bl->isloop) { bl = bl->previous; }
+  if (!bl)
+    luaX_syntaxerror(ls, "no loop to break");
+  luaK_concat(fs, &fs->bl->brks, pc);
 }
 }
 
 
 
 
@@ -1248,12 +1269,13 @@ static void labelstat (LexState *ls, TString *label, int line) {
   checknext(ls, TK_DBCOLON);  /* skip double colon */
   checknext(ls, TK_DBCOLON);  /* skip double colon */
   /* create new entry for this label */
   /* create new entry for this label */
   l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
   l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs));
+  luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
   skipnoopstat(ls);  /* skip other no-op statements */
   skipnoopstat(ls);  /* skip other no-op statements */
   if (block_follow(ls, 0)) {  /* label is last no-op statement in the block? */
   if (block_follow(ls, 0)) {  /* label is last no-op statement in the block? */
     /* assume that locals are already out of scope */
     /* assume that locals are already out of scope */
     ll->arr[l].nactvar = fs->bl->nactvar;
     ll->arr[l].nactvar = fs->bl->nactvar;
   }
   }
-  findgotos(ls, &ll->arr[l]);
+  solvegotos(ls, &ll->arr[l]);
 }
 }
 
 
 
 
@@ -1289,8 +1311,15 @@ static void repeatstat (LexState *ls, int line) {
   check_match(ls, TK_UNTIL, TK_REPEAT, line);
   check_match(ls, TK_UNTIL, TK_REPEAT, line);
   condexit = cond(ls);  /* read condition (inside scope block) */
   condexit = cond(ls);  /* read condition (inside scope block) */
   if (bl2.upval)  /* upvalues? */
   if (bl2.upval)  /* upvalues? */
-    luaK_patchclose(fs, condexit, bl2.nactvar);
+    luaK_patchclose(fs, condexit);
   leaveblock(fs);  /* finish scope */
   leaveblock(fs);  /* finish scope */
+  if (bl2.upval) {  /* upvalues? */
+    int exit = luaK_jump(fs);  /* normal exit must jump over fix */
+    luaK_patchtohere(fs, condexit);  /* repetition must close upvalues */
+    luaK_codeABC(fs, OP_CLOSE, bl2.nactvar, 0, 0);
+    condexit = luaK_jump(fs);  /* repeat after closing upvalues */
+    luaK_patchtohere(fs, exit);  /* normal exit comes to here */
+  }
   luaK_patchlist(fs, condexit, repeat_init);  /* close the loop */
   luaK_patchlist(fs, condexit, repeat_init);  /* close the loop */
   leaveblock(fs);  /* finish loop */
   leaveblock(fs);  /* finish loop */
 }
 }
@@ -1428,9 +1457,12 @@ static void test_then_block (LexState *ls, int *escapelist) {
   if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) {
   if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) {
     luaK_goiffalse(ls->fs, &v);  /* will jump to label if condition is true */
     luaK_goiffalse(ls->fs, &v);  /* will jump to label if condition is true */
     enterblock(fs, &bl, 0);  /* must enter block before 'goto' */
     enterblock(fs, &bl, 0);  /* must enter block before 'goto' */
-    gotostat(ls, v.t);  /* handle goto/break */
+    if (ls->t.token == TK_GOTO)
+      gotostat(ls, v.t);  /* handle goto */
+    else
+      breakstat(ls, v.t);  /* handle break */
     while (testnext(ls, ';')) {}  /* skip semicolons */
     while (testnext(ls, ';')) {}  /* skip semicolons */
-    if (block_follow(ls, 0)) {  /* 'goto' is the entire block? */
+    if (block_follow(ls, 0)) {  /* 'goto'/'break' is the entire block? */
       leaveblock(fs);
       leaveblock(fs);
       return;  /* and that is it */
       return;  /* and that is it */
     }
     }
@@ -1623,7 +1655,10 @@ static void statement (LexState *ls) {
       retstat(ls);
       retstat(ls);
       break;
       break;
     }
     }
-    case TK_BREAK:   /* stat -> breakstat */
+    case TK_BREAK: {  /* stat -> breakstat */
+      breakstat(ls, luaK_jump(ls->fs));
+      break;
+    }
     case TK_GOTO: {  /* stat -> 'goto' NAME */
     case TK_GOTO: {  /* stat -> 'goto' NAME */
       gotostat(ls, luaK_jump(ls->fs));
       gotostat(ls, luaK_jump(ls->fs));
       break;
       break;

+ 6 - 5
lvm.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lvm.c,v 2.290 2017/07/07 16:34:32 roberto Exp roberto $
+** $Id: lvm.c,v 2.291 2017/08/14 18:33:14 roberto Exp roberto $
 ** Lua virtual machine
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -753,10 +753,7 @@ void luaV_finishOp (lua_State *L) {
 ** Execute a jump instruction. The 'updatemask' allows signals to stop
 ** Execute a jump instruction. The 'updatemask' allows signals to stop
 ** tight loops. (Without it, the local copy of 'mask' could never change.)
 ** tight loops. (Without it, the local copy of 'mask' could never change.)
 */
 */
-#define dojump(ci,i,e) \
-  { int a = GETARG_A(i); \
-    if (a != 0) luaF_close(L, ci->func + a); \
-    pc += GETARG_sBx(i) + e; updatemask(L); }
+#define dojump(ci,i,e)	{ pc += GETARG_sBx(i) + e; updatemask(L); }
 
 
 
 
 /* for test instructions, execute the jump instruction that follows it */
 /* for test instructions, execute the jump instruction that follows it */
@@ -1201,6 +1198,10 @@ void luaV_execute (lua_State *L) {
         L->top = ci->top;  /* restore top */
         L->top = ci->top;  /* restore top */
         vmbreak;
         vmbreak;
       }
       }
+      vmcase(OP_CLOSE) {
+        luaF_close(L, ra);
+        vmbreak;
+      }
       vmcase(OP_JMP) {
       vmcase(OP_JMP) {
         dojump(ci, i, 0);
         dojump(ci, i, 0);
         vmbreak;
         vmbreak;