diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-09-13 16:50:08 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2017-09-13 16:50:08 -0300 |
commit | 80d9b09f351c7a9be557116e9c79ae11e9b3f032 (patch) | |
tree | a6ae7cce019e85c2f79987035c77d4e4213d07a9 | |
parent | 029d269f4d1afd0e9ea0f889eb3e65a7f0fab0f9 (diff) | |
download | lua-80d9b09f351c7a9be557116e9c79ae11e9b3f032.tar.gz lua-80d9b09f351c7a9be557116e9c79ae11e9b3f032.tar.bz2 lua-80d9b09f351c7a9be557116e9c79ae11e9b3f032.zip |
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)
-rw-r--r-- | lcode.c | 51 | ||||
-rw-r--r-- | lcode.h | 6 | ||||
-rw-r--r-- | lopcodes.c | 6 | ||||
-rw-r--r-- | lopcodes.h | 5 | ||||
-rw-r--r-- | lparser.c | 127 | ||||
-rw-r--r-- | lvm.c | 11 |
6 files changed, 139 insertions, 67 deletions
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lcode.c,v 2.120 2017/06/27 11:35:31 roberto Exp roberto $ | 2 | ** $Id: lcode.c,v 2.121 2017/06/29 15:06:44 roberto Exp roberto $ |
3 | ** Code generator for Lua | 3 | ** Code generator for Lua |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -272,20 +272,51 @@ void luaK_patchlist (FuncState *fs, int list, int target) { | |||
272 | 272 | ||
273 | 273 | ||
274 | /* | 274 | /* |
275 | ** Path all jumps in 'list' to close upvalues up to given 'level' | 275 | ** Check whether some jump in given list needs a close instruction. |
276 | ** (The assertion checks that jumps either were closing nothing | ||
277 | ** or were closing higher levels, from inner blocks.) | ||
278 | */ | 276 | */ |
279 | void luaK_patchclose (FuncState *fs, int list, int level) { | 277 | int luaK_needclose (FuncState *fs, int list) { |
280 | level++; /* argument is +1 to reserve 0 as non-op */ | ||
281 | for (; list != NO_JUMP; list = getjump(fs, list)) { | 278 | for (; list != NO_JUMP; list = getjump(fs, list)) { |
282 | lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP && | 279 | if (GETARG_A(fs->f->code[list])) /* needs close? */ |
283 | (GETARG_A(fs->f->code[list]) == 0 || | 280 | return 1; |
284 | GETARG_A(fs->f->code[list]) >= level)); | 281 | } |
285 | SETARG_A(fs->f->code[list], level); | 282 | return 0; |
283 | } | ||
284 | |||
285 | |||
286 | /* | ||
287 | ** Correct a jump list to jump to 'target'. If 'hasclose' is true, | ||
288 | ** 'target' contains an OP_CLOSE instruction (see first assert). | ||
289 | ** Only jumps with the A arg true need that close; other jumps | ||
290 | ** avoid it jumping to the next instruction. | ||
291 | */ | ||
292 | void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose) { | ||
293 | lua_assert(!hasclose || GET_OPCODE(fs->f->code[target]) == OP_CLOSE); | ||
294 | while (list != NO_JUMP) { | ||
295 | int next = getjump(fs, list); | ||
296 | lua_assert(!GETARG_A(fs->f->code[list]) || hasclose); | ||
297 | patchtestreg(fs, list, NO_REG); /* do not generate values */ | ||
298 | if (!hasclose || GETARG_A(fs->f->code[list])) | ||
299 | fixjump(fs, list, target); | ||
300 | else /* there is a CLOSE instruction but jump does not need it */ | ||
301 | fixjump(fs, list, target + 1); /* avoid CLOSE instruction */ | ||
302 | list = next; | ||
286 | } | 303 | } |
287 | } | 304 | } |
288 | 305 | ||
306 | |||
307 | /* | ||
308 | ** Mark (using the A arg) all jumps in 'list' to close upvalues. Mark | ||
309 | ** will instruct 'luaK_patchgoto' to make these jumps go to OP_CLOSE | ||
310 | ** instructions. | ||
311 | */ | ||
312 | void luaK_patchclose (FuncState *fs, int list) { | ||
313 | for (; list != NO_JUMP; list = getjump(fs, list)) { | ||
314 | lua_assert(GET_OPCODE(fs->f->code[list]) == OP_JMP); | ||
315 | SETARG_A(fs->f->code[list], 1); | ||
316 | } | ||
317 | } | ||
318 | |||
319 | |||
289 | #if !defined(MAXIWTHABS) | 320 | #if !defined(MAXIWTHABS) |
290 | #define MAXIWTHABS 120 | 321 | #define MAXIWTHABS 120 |
291 | #endif | 322 | #endif |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lcode.h,v 1.64 2016/01/05 16:22:37 roberto Exp roberto $ | 2 | ** $Id: lcode.h,v 1.65 2017/04/20 19:53:55 roberto Exp roberto $ |
3 | ** Code generator for Lua | 3 | ** Code generator for Lua |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -73,8 +73,10 @@ LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); | |||
73 | LUAI_FUNC int luaK_jump (FuncState *fs); | 73 | LUAI_FUNC int luaK_jump (FuncState *fs); |
74 | LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); | 74 | LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); |
75 | LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); | 75 | LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); |
76 | void luaK_patchgoto (FuncState *fs, int list, int target, int hasclose); | ||
76 | LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); | 77 | LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); |
77 | LUAI_FUNC void luaK_patchclose (FuncState *fs, int list, int level); | 78 | LUAI_FUNC void luaK_patchclose (FuncState *fs, int list); |
79 | LUAI_FUNC int luaK_needclose (FuncState *fs, int list); | ||
78 | LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); | 80 | LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); |
79 | LUAI_FUNC int luaK_getlabel (FuncState *fs); | 81 | LUAI_FUNC int luaK_getlabel (FuncState *fs); |
80 | LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); | 82 | LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v, int line); |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lopcodes.c,v 1.59 2017/06/29 15:38:41 roberto Exp roberto $ | 2 | ** $Id: lopcodes.c,v 1.60 2017/08/14 18:33:14 roberto Exp roberto $ |
3 | ** Opcodes for Lua virtual machine | 3 | ** Opcodes for Lua virtual machine |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -54,6 +54,7 @@ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { | |||
54 | "NOT", | 54 | "NOT", |
55 | "LEN", | 55 | "LEN", |
56 | "CONCAT", | 56 | "CONCAT", |
57 | "CLOSE", | ||
57 | "JMP", | 58 | "JMP", |
58 | "EQ", | 59 | "EQ", |
59 | "LT", | 60 | "LT", |
@@ -115,7 +116,8 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { | |||
115 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ | 116 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ |
116 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ | 117 | ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ |
117 | ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ | 118 | ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ |
118 | ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ | 119 | ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ |
120 | ,opmode(0, 0, OpArgU, OpArgN, iAsBx) /* OP_JMP */ | ||
119 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ | 121 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ |
120 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ | 122 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ |
121 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ | 123 | ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lopcodes.h,v 1.155 2017/06/29 15:38:41 roberto Exp roberto $ | 2 | ** $Id: lopcodes.h,v 1.156 2017/08/14 18:33:14 roberto Exp roberto $ |
3 | ** Opcodes for Lua virtual machine | 3 | ** Opcodes for Lua virtual machine |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -217,7 +217,8 @@ OP_LEN,/* A B R(A) := length of R(B) */ | |||
217 | 217 | ||
218 | OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ | 218 | OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */ |
219 | 219 | ||
220 | OP_JMP,/* A sBx pc+=sBx; if (A) close all upvalues >= R(A - 1) */ | 220 | OP_CLOSE,/* A close all upvalues >= R(A) */ |
221 | OP_JMP,/* sBx pc+=sBx */ | ||
221 | OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ | 222 | OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */ |
222 | OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ | 223 | OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */ |
223 | OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ | 224 | OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */ |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lparser.c,v 2.163 2017/08/12 13:12:21 roberto Exp roberto $ | 2 | ** $Id: lparser.c,v 2.164 2017/08/14 18:33:14 roberto Exp roberto $ |
3 | ** Lua Parser | 3 | ** Lua Parser |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -49,6 +49,8 @@ typedef struct BlockCnt { | |||
49 | struct BlockCnt *previous; /* chain */ | 49 | struct BlockCnt *previous; /* chain */ |
50 | int firstlabel; /* index of first label in this block */ | 50 | int firstlabel; /* index of first label in this block */ |
51 | int firstgoto; /* index of first pending goto in this block */ | 51 | int firstgoto; /* index of first pending goto in this block */ |
52 | int brks; /* list of break jumps in this block */ | ||
53 | lu_byte brkcls; /* true if some 'break' needs to close upvalues */ | ||
52 | lu_byte nactvar; /* # active locals outside the block */ | 54 | lu_byte nactvar; /* # active locals outside the block */ |
53 | lu_byte upval; /* true if some variable in the block is an upvalue */ | 55 | lu_byte upval; /* true if some variable in the block is an upvalue */ |
54 | lu_byte isloop; /* true if 'block' is a loop */ | 56 | lu_byte isloop; /* true if 'block' is a loop */ |
@@ -351,7 +353,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) { | |||
351 | getstr(gt->name), gt->line, getstr(vname)); | 353 | getstr(gt->name), gt->line, getstr(vname)); |
352 | semerror(ls, msg); | 354 | semerror(ls, msg); |
353 | } | 355 | } |
354 | luaK_patchlist(fs, gt->pc, label->pc); | 356 | luaK_patchgoto(fs, gt->pc, label->pc, 1); |
355 | /* remove goto from pending list */ | 357 | /* remove goto from pending list */ |
356 | for (i = g; i < gl->n - 1; i++) | 358 | for (i = g; i < gl->n - 1; i++) |
357 | gl->arr[i] = gl->arr[i + 1]; | 359 | gl->arr[i] = gl->arr[i + 1]; |
@@ -362,7 +364,7 @@ static void closegoto (LexState *ls, int g, Labeldesc *label) { | |||
362 | /* | 364 | /* |
363 | ** try to close a goto with existing labels; this solves backward jumps | 365 | ** try to close a goto with existing labels; this solves backward jumps |
364 | */ | 366 | */ |
365 | static int findlabel (LexState *ls, int g) { | 367 | static int solvelabel (LexState *ls, int g) { |
366 | int i; | 368 | int i; |
367 | BlockCnt *bl = ls->fs->bl; | 369 | BlockCnt *bl = ls->fs->bl; |
368 | Dyndata *dyd = ls->dyd; | 370 | Dyndata *dyd = ls->dyd; |
@@ -373,7 +375,7 @@ static int findlabel (LexState *ls, int g) { | |||
373 | if (eqstr(lb->name, gt->name)) { /* correct label? */ | 375 | if (eqstr(lb->name, gt->name)) { /* correct label? */ |
374 | if (gt->nactvar > lb->nactvar && | 376 | if (gt->nactvar > lb->nactvar && |
375 | (bl->upval || dyd->label.n > bl->firstlabel)) | 377 | (bl->upval || dyd->label.n > bl->firstlabel)) |
376 | luaK_patchclose(ls->fs, gt->pc, lb->nactvar); | 378 | luaK_patchclose(ls->fs, gt->pc); |
377 | closegoto(ls, g, lb); /* close it */ | 379 | closegoto(ls, g, lb); /* close it */ |
378 | return 1; | 380 | return 1; |
379 | } | 381 | } |
@@ -400,12 +402,12 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name, | |||
400 | ** check whether new label 'lb' matches any pending gotos in current | 402 | ** check whether new label 'lb' matches any pending gotos in current |
401 | ** block; solves forward jumps | 403 | ** block; solves forward jumps |
402 | */ | 404 | */ |
403 | static void findgotos (LexState *ls, Labeldesc *lb) { | 405 | static void solvegotos (LexState *ls, Labeldesc *lb) { |
404 | Labellist *gl = &ls->dyd->gt; | 406 | Labellist *gl = &ls->dyd->gt; |
405 | int i = ls->fs->bl->firstgoto; | 407 | int i = ls->fs->bl->firstgoto; |
406 | while (i < gl->n) { | 408 | while (i < gl->n) { |
407 | if (eqstr(gl->arr[i].name, lb->name)) | 409 | if (eqstr(gl->arr[i].name, lb->name)) |
408 | closegoto(ls, i, lb); | 410 | closegoto(ls, i, lb); /* will remove 'i' from the list */ |
409 | else | 411 | else |
410 | i++; | 412 | i++; |
411 | } | 413 | } |
@@ -416,23 +418,32 @@ static void findgotos (LexState *ls, Labeldesc *lb) { | |||
416 | ** export pending gotos to outer level, to check them against | 418 | ** export pending gotos to outer level, to check them against |
417 | ** outer labels; if the block being exited has upvalues, and | 419 | ** outer labels; if the block being exited has upvalues, and |
418 | ** the goto exits the scope of any variable (which can be the | 420 | ** the goto exits the scope of any variable (which can be the |
419 | ** upvalue), close those variables being exited. | 421 | ** upvalue), close those variables being exited. Also export |
422 | ** break list. | ||
420 | */ | 423 | */ |
421 | static void movegotosout (FuncState *fs, BlockCnt *bl) { | 424 | static void movegotosout (FuncState *fs, BlockCnt *bl) { |
422 | int i = bl->firstgoto; | 425 | int i = bl->firstgoto; |
423 | Labellist *gl = &fs->ls->dyd->gt; | 426 | Labellist *gl = &fs->ls->dyd->gt; |
424 | /* correct pending gotos to current block and try to close it | 427 | /* correct pending gotos to current block and try to close it |
425 | with visible labels */ | 428 | with visible labels */ |
426 | while (i < gl->n) { | 429 | while (i < gl->n) { /* for each pending goto */ |
427 | Labeldesc *gt = &gl->arr[i]; | 430 | Labeldesc *gt = &gl->arr[i]; |
428 | if (gt->nactvar > bl->nactvar) { | 431 | if (gt->nactvar > bl->nactvar) { /* leaving a variable scope? */ |
429 | if (bl->upval) | 432 | if (bl->upval) /* variable may be an upvalue? */ |
430 | luaK_patchclose(fs, gt->pc, bl->nactvar); | 433 | luaK_patchclose(fs, gt->pc); /* jump will need a close */ |
431 | gt->nactvar = bl->nactvar; | 434 | gt->nactvar = bl->nactvar; /* update goto level */ |
432 | } | 435 | } |
433 | if (!findlabel(fs->ls, i)) | 436 | if (!solvelabel(fs->ls, i)) |
434 | i++; /* move to next one */ | 437 | i++; /* move to next one */ |
438 | /* else, 'solvelabel' removed current goto from the list | ||
439 | and 'i' now points to next one */ | ||
435 | } | 440 | } |
441 | /* handles break list */ | ||
442 | if (bl->upval) /* exiting the scope of an upvalue? */ | ||
443 | luaK_patchclose(fs, bl->brks); /* breaks will need OP_CLOSE */ | ||
444 | /* move breaks to outer block */ | ||
445 | luaK_concat(fs, &bl->previous->brks, bl->brks); | ||
446 | bl->previous->brkcls |= bl->brkcls; | ||
436 | } | 447 | } |
437 | 448 | ||
438 | 449 | ||
@@ -441,6 +452,8 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { | |||
441 | bl->nactvar = fs->nactvar; | 452 | bl->nactvar = fs->nactvar; |
442 | bl->firstlabel = fs->ls->dyd->label.n; | 453 | bl->firstlabel = fs->ls->dyd->label.n; |
443 | bl->firstgoto = fs->ls->dyd->gt.n; | 454 | bl->firstgoto = fs->ls->dyd->gt.n; |
455 | bl->brks = NO_JUMP; | ||
456 | bl->brkcls = 0; | ||
444 | bl->upval = 0; | 457 | bl->upval = 0; |
445 | bl->previous = fs->bl; | 458 | bl->previous = fs->bl; |
446 | fs->bl = bl; | 459 | fs->bl = bl; |
@@ -449,22 +462,24 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) { | |||
449 | 462 | ||
450 | 463 | ||
451 | /* | 464 | /* |
452 | ** create a label named 'break' to resolve break statements | 465 | ** Fix all breaks in block 'bl' to jump to the end of the block. |
453 | */ | 466 | */ |
454 | static void breaklabel (LexState *ls) { | 467 | static void fixbreaks (FuncState *fs, BlockCnt *bl) { |
455 | TString *n = luaS_new(ls->L, "break"); | 468 | int target = fs->pc; |
456 | int l = newlabelentry(ls, &ls->dyd->label, n, 0, ls->fs->pc); | 469 | if (bl->brkcls) /* does the block need to close upvalues? */ |
457 | findgotos(ls, &ls->dyd->label.arr[l]); | 470 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); |
471 | luaK_patchgoto(fs, bl->brks, target, bl->brkcls); | ||
472 | bl->brks = NO_JUMP; /* no more breaks to fix */ | ||
473 | bl->brkcls = 0; /* no more need to close upvalues */ | ||
474 | lua_assert(!bl->upval); /* loop body cannot have local variables */ | ||
458 | } | 475 | } |
459 | 476 | ||
477 | |||
460 | /* | 478 | /* |
461 | ** generates an error for an undefined 'goto'; choose appropriate | 479 | ** generates an error for an undefined 'goto'. |
462 | ** message when label name is a reserved word (which can only be 'break') | ||
463 | */ | 480 | */ |
464 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | 481 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { |
465 | const char *msg = isreserved(gt->name) | 482 | const char *msg = "no visible label '%s' for <goto> at line %d"; |
466 | ? "<%s> at line %d not inside a loop" | ||
467 | : "no visible label '%s' for <goto> at line %d"; | ||
468 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); | 483 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); |
469 | semerror(ls, msg); | 484 | semerror(ls, msg); |
470 | } | 485 | } |
@@ -473,14 +488,12 @@ static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | |||
473 | static void leaveblock (FuncState *fs) { | 488 | static void leaveblock (FuncState *fs) { |
474 | BlockCnt *bl = fs->bl; | 489 | BlockCnt *bl = fs->bl; |
475 | LexState *ls = fs->ls; | 490 | LexState *ls = fs->ls; |
476 | if (bl->previous && bl->upval) { | 491 | if (bl->upval && bl->brks != NO_JUMP) /* breaks in upvalue scopes? */ |
477 | /* create a 'jump to here' to close upvalues */ | 492 | bl->brkcls = 1; /* these breaks must close the upvalues */ |
478 | int j = luaK_jump(fs); | ||
479 | luaK_patchclose(fs, j, bl->nactvar); | ||
480 | luaK_patchtohere(fs, j); | ||
481 | } | ||
482 | if (bl->isloop) | 493 | if (bl->isloop) |
483 | breaklabel(ls); /* close pending breaks */ | 494 | fixbreaks(fs, bl); /* fix pending breaks */ |
495 | if (bl->previous && bl->upval) | ||
496 | luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); | ||
484 | fs->bl = bl->previous; | 497 | fs->bl = bl->previous; |
485 | removevars(fs, bl->nactvar); | 498 | removevars(fs, bl->nactvar); |
486 | lua_assert(bl->nactvar == fs->nactvar); | 499 | lua_assert(bl->nactvar == fs->nactvar); |
@@ -488,8 +501,11 @@ static void leaveblock (FuncState *fs) { | |||
488 | ls->dyd->label.n = bl->firstlabel; /* remove local labels */ | 501 | ls->dyd->label.n = bl->firstlabel; /* remove local labels */ |
489 | if (bl->previous) /* inner block? */ | 502 | if (bl->previous) /* inner block? */ |
490 | movegotosout(fs, bl); /* update pending gotos to outer block */ | 503 | movegotosout(fs, bl); /* update pending gotos to outer block */ |
491 | else if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ | 504 | else { |
492 | undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ | 505 | lua_assert(bl->brks == NO_JUMP); /* no pending breaks */ |
506 | if (bl->firstgoto < ls->dyd->gt.n) /* pending gotos in outer block? */ | ||
507 | undefgoto(ls, &ls->dyd->gt.arr[bl->firstgoto]); /* error */ | ||
508 | } | ||
493 | } | 509 | } |
494 | 510 | ||
495 | 511 | ||
@@ -1205,16 +1221,21 @@ static int cond (LexState *ls) { | |||
1205 | 1221 | ||
1206 | static void gotostat (LexState *ls, int pc) { | 1222 | static void gotostat (LexState *ls, int pc) { |
1207 | int line = ls->linenumber; | 1223 | int line = ls->linenumber; |
1208 | TString *label; | ||
1209 | int g; | 1224 | int g; |
1210 | if (testnext(ls, TK_GOTO)) | 1225 | luaX_next(ls); /* skip 'goto' */ |
1211 | label = str_checkname(ls); | 1226 | g = newlabelentry(ls, &ls->dyd->gt, str_checkname(ls), line, pc); |
1212 | else { | 1227 | solvelabel(ls, g); /* close it if label already defined */ |
1213 | luaX_next(ls); /* skip break */ | 1228 | } |
1214 | label = luaS_new(ls->L, "break"); | 1229 | |
1215 | } | 1230 | |
1216 | g = newlabelentry(ls, &ls->dyd->gt, label, line, pc); | 1231 | static void breakstat (LexState *ls, int pc) { |
1217 | findlabel(ls, g); /* close it if label already defined */ | 1232 | FuncState *fs = ls->fs; |
1233 | BlockCnt *bl = fs->bl; | ||
1234 | luaX_next(ls); /* skip break */ | ||
1235 | while (bl && !bl->isloop) { bl = bl->previous; } | ||
1236 | if (!bl) | ||
1237 | luaX_syntaxerror(ls, "no loop to break"); | ||
1238 | luaK_concat(fs, &fs->bl->brks, pc); | ||
1218 | } | 1239 | } |
1219 | 1240 | ||
1220 | 1241 | ||
@@ -1248,12 +1269,13 @@ static void labelstat (LexState *ls, TString *label, int line) { | |||
1248 | checknext(ls, TK_DBCOLON); /* skip double colon */ | 1269 | checknext(ls, TK_DBCOLON); /* skip double colon */ |
1249 | /* create new entry for this label */ | 1270 | /* create new entry for this label */ |
1250 | l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); | 1271 | l = newlabelentry(ls, ll, label, line, luaK_getlabel(fs)); |
1272 | luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0); | ||
1251 | skipnoopstat(ls); /* skip other no-op statements */ | 1273 | skipnoopstat(ls); /* skip other no-op statements */ |
1252 | if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ | 1274 | if (block_follow(ls, 0)) { /* label is last no-op statement in the block? */ |
1253 | /* assume that locals are already out of scope */ | 1275 | /* assume that locals are already out of scope */ |
1254 | ll->arr[l].nactvar = fs->bl->nactvar; | 1276 | ll->arr[l].nactvar = fs->bl->nactvar; |
1255 | } | 1277 | } |
1256 | findgotos(ls, &ll->arr[l]); | 1278 | solvegotos(ls, &ll->arr[l]); |
1257 | } | 1279 | } |
1258 | 1280 | ||
1259 | 1281 | ||
@@ -1289,8 +1311,15 @@ static void repeatstat (LexState *ls, int line) { | |||
1289 | check_match(ls, TK_UNTIL, TK_REPEAT, line); | 1311 | check_match(ls, TK_UNTIL, TK_REPEAT, line); |
1290 | condexit = cond(ls); /* read condition (inside scope block) */ | 1312 | condexit = cond(ls); /* read condition (inside scope block) */ |
1291 | if (bl2.upval) /* upvalues? */ | 1313 | if (bl2.upval) /* upvalues? */ |
1292 | luaK_patchclose(fs, condexit, bl2.nactvar); | 1314 | luaK_patchclose(fs, condexit); |
1293 | leaveblock(fs); /* finish scope */ | 1315 | leaveblock(fs); /* finish scope */ |
1316 | if (bl2.upval) { /* upvalues? */ | ||
1317 | int exit = luaK_jump(fs); /* normal exit must jump over fix */ | ||
1318 | luaK_patchtohere(fs, condexit); /* repetition must close upvalues */ | ||
1319 | luaK_codeABC(fs, OP_CLOSE, bl2.nactvar, 0, 0); | ||
1320 | condexit = luaK_jump(fs); /* repeat after closing upvalues */ | ||
1321 | luaK_patchtohere(fs, exit); /* normal exit comes to here */ | ||
1322 | } | ||
1294 | luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ | 1323 | luaK_patchlist(fs, condexit, repeat_init); /* close the loop */ |
1295 | leaveblock(fs); /* finish loop */ | 1324 | leaveblock(fs); /* finish loop */ |
1296 | } | 1325 | } |
@@ -1428,9 +1457,12 @@ static void test_then_block (LexState *ls, int *escapelist) { | |||
1428 | if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { | 1457 | if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { |
1429 | luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ | 1458 | luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ |
1430 | enterblock(fs, &bl, 0); /* must enter block before 'goto' */ | 1459 | enterblock(fs, &bl, 0); /* must enter block before 'goto' */ |
1431 | gotostat(ls, v.t); /* handle goto/break */ | 1460 | if (ls->t.token == TK_GOTO) |
1461 | gotostat(ls, v.t); /* handle goto */ | ||
1462 | else | ||
1463 | breakstat(ls, v.t); /* handle break */ | ||
1432 | while (testnext(ls, ';')) {} /* skip semicolons */ | 1464 | while (testnext(ls, ';')) {} /* skip semicolons */ |
1433 | if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ | 1465 | if (block_follow(ls, 0)) { /* 'goto'/'break' is the entire block? */ |
1434 | leaveblock(fs); | 1466 | leaveblock(fs); |
1435 | return; /* and that is it */ | 1467 | return; /* and that is it */ |
1436 | } | 1468 | } |
@@ -1623,7 +1655,10 @@ static void statement (LexState *ls) { | |||
1623 | retstat(ls); | 1655 | retstat(ls); |
1624 | break; | 1656 | break; |
1625 | } | 1657 | } |
1626 | case TK_BREAK: /* stat -> breakstat */ | 1658 | case TK_BREAK: { /* stat -> breakstat */ |
1659 | breakstat(ls, luaK_jump(ls->fs)); | ||
1660 | break; | ||
1661 | } | ||
1627 | case TK_GOTO: { /* stat -> 'goto' NAME */ | 1662 | case TK_GOTO: { /* stat -> 'goto' NAME */ |
1628 | gotostat(ls, luaK_jump(ls->fs)); | 1663 | gotostat(ls, luaK_jump(ls->fs)); |
1629 | break; | 1664 | break; |
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | ** $Id: lvm.c,v 2.290 2017/07/07 16:34:32 roberto Exp roberto $ | 2 | ** $Id: lvm.c,v 2.291 2017/08/14 18:33:14 roberto Exp roberto $ |
3 | ** Lua virtual machine | 3 | ** Lua virtual machine |
4 | ** See Copyright Notice in lua.h | 4 | ** See Copyright Notice in lua.h |
5 | */ | 5 | */ |
@@ -753,10 +753,7 @@ void luaV_finishOp (lua_State *L) { | |||
753 | ** Execute a jump instruction. The 'updatemask' allows signals to stop | 753 | ** Execute a jump instruction. The 'updatemask' allows signals to stop |
754 | ** tight loops. (Without it, the local copy of 'mask' could never change.) | 754 | ** tight loops. (Without it, the local copy of 'mask' could never change.) |
755 | */ | 755 | */ |
756 | #define dojump(ci,i,e) \ | 756 | #define dojump(ci,i,e) { pc += GETARG_sBx(i) + e; updatemask(L); } |
757 | { int a = GETARG_A(i); \ | ||
758 | if (a != 0) luaF_close(L, ci->func + a); \ | ||
759 | pc += GETARG_sBx(i) + e; updatemask(L); } | ||
760 | 757 | ||
761 | 758 | ||
762 | /* for test instructions, execute the jump instruction that follows it */ | 759 | /* for test instructions, execute the jump instruction that follows it */ |
@@ -1201,6 +1198,10 @@ void luaV_execute (lua_State *L) { | |||
1201 | L->top = ci->top; /* restore top */ | 1198 | L->top = ci->top; /* restore top */ |
1202 | vmbreak; | 1199 | vmbreak; |
1203 | } | 1200 | } |
1201 | vmcase(OP_CLOSE) { | ||
1202 | luaF_close(L, ra); | ||
1203 | vmbreak; | ||
1204 | } | ||
1204 | vmcase(OP_JMP) { | 1205 | vmcase(OP_JMP) { |
1205 | dojump(ci, i, 0); | 1206 | dojump(ci, i, 0); |
1206 | vmbreak; | 1207 | vmbreak; |