aboutsummaryrefslogtreecommitdiff
path: root/lparser.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2017-09-13 16:50:08 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2017-09-13 16:50:08 -0300
commit80d9b09f351c7a9be557116e9c79ae11e9b3f032 (patch)
treea6ae7cce019e85c2f79987035c77d4e4213d07a9 /lparser.c
parent029d269f4d1afd0e9ea0f889eb3e65a7f0fab0f9 (diff)
downloadlua-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.c127
1 files changed, 81 insertions, 46 deletions
diff --git a/lparser.c b/lparser.c
index 2eb5581f..7fd62f50 100644
--- a/lparser.c
+++ b/lparser.c
@@ -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*/
365static int findlabel (LexState *ls, int g) { 367static 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*/
403static void findgotos (LexState *ls, Labeldesc *lb) { 405static 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*/
421static void movegotosout (FuncState *fs, BlockCnt *bl) { 424static 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*/
454static void breaklabel (LexState *ls) { 467static 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*/
464static l_noret undefgoto (LexState *ls, Labeldesc *gt) { 481static 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) {
473static void leaveblock (FuncState *fs) { 488static 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
1206static void gotostat (LexState *ls, int pc) { 1222static 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); 1231static 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;