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 /lparser.c | |
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)
Diffstat (limited to 'lparser.c')
-rw-r--r-- | lparser.c | 127 |
1 files changed, 81 insertions, 46 deletions
@@ -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; |