aboutsummaryrefslogtreecommitdiff
path: root/lparser.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-30 15:04:19 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-30 15:04:19 -0300
commit2316ec4c24a475e091ec3153a5bd908801a3a109 (patch)
treece14172ed7d7dc8874ea282a97d41f4b380f419e /lparser.c
parenta006514ea138a29b6031058d9002b48a572b5dd6 (diff)
downloadlua-2316ec4c24a475e091ec3153a5bd908801a3a109.tar.gz
lua-2316ec4c24a475e091ec3153a5bd908801a3a109.tar.bz2
lua-2316ec4c24a475e091ec3153a5bd908801a3a109.zip
Back with optimization for 'if cond then goto'
Statements like 'if cond then goto label' generate code so that the jump in the 'if' goes directly to the given label. This optimization cannot be done when the jump is backwards leaving the scope of some variable, as it cannot add the needed 'close' instruction. (The jumps were already generated by the 'if'.) This commit also added 'likely'/'unlikely' for tests for errors in the parser, and it changed the way breaks outside loops are detected. (Now they are detected like other goto's with undefined labels.)
Diffstat (limited to 'lparser.c')
-rw-r--r--lparser.c84
1 files changed, 65 insertions, 19 deletions
diff --git a/lparser.c b/lparser.c
index 499e9531..9419f880 100644
--- a/lparser.c
+++ b/lparser.c
@@ -113,7 +113,7 @@ static void checknext (LexState *ls, int c) {
113 113
114 114
115static void check_match (LexState *ls, int what, int who, int where) { 115static void check_match (LexState *ls, int what, int who, int where) {
116 if (!testnext(ls, what)) { 116 if (unlikely(!testnext(ls, what))) {
117 if (where == ls->linenumber) 117 if (where == ls->linenumber)
118 error_expected(ls, what); 118 error_expected(ls, what);
119 else { 119 else {
@@ -350,7 +350,7 @@ static void solvegoto (LexState *ls, int g, Labeldesc *label) {
350 Labellist *gl = &ls->dyd->gt; /* list of goto's */ 350 Labellist *gl = &ls->dyd->gt; /* list of goto's */
351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */ 351 Labeldesc *gt = &gl->arr[g]; /* goto to be resolved */
352 lua_assert(eqstr(gt->name, label->name)); 352 lua_assert(eqstr(gt->name, label->name));
353 if (gt->nactvar < label->nactvar) /* enter some scope? */ 353 if (unlikely(gt->nactvar < label->nactvar)) /* enter some scope? */
354 jumpscopeerror(ls, gt); 354 jumpscopeerror(ls, gt);
355 luaK_patchlist(ls->fs, gt->pc, label->pc); 355 luaK_patchlist(ls->fs, gt->pc, label->pc);
356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */ 356 for (i = g; i < gl->n - 1; i++) /* remove goto from pending list */
@@ -393,6 +393,11 @@ static int newlabelentry (LexState *ls, Labellist *l, TString *name,
393} 393}
394 394
395 395
396static int newgotoentry (LexState *ls, TString *name, int line, int pc) {
397 return newlabelentry(ls, &ls->dyd->gt, name, line, pc);
398}
399
400
396/* 401/*
397** Solves forward jumps. Check whether new label 'lb' matches any 402** Solves forward jumps. Check whether new label 'lb' matches any
398** pending gotos in current block and solves them. Return true 403** pending gotos in current block and solves them. Return true
@@ -471,8 +476,15 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
471** generates an error for an undefined 'goto'. 476** generates an error for an undefined 'goto'.
472*/ 477*/
473static l_noret undefgoto (LexState *ls, Labeldesc *gt) { 478static l_noret undefgoto (LexState *ls, Labeldesc *gt) {
474 const char *msg = "no visible label '%s' for <goto> at line %d"; 479 const char *msg;
475 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line); 480 if (eqstr(gt->name, luaS_newliteral(ls->L, "break"))) {
481 msg = "break outside loop at line %d";
482 msg = luaO_pushfstring(ls->L, msg, gt->line);
483 }
484 else {
485 msg = "no visible label '%s' for <goto> at line %d";
486 msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line);
487 }
476 luaK_semerror(ls, msg); 488 luaK_semerror(ls, msg);
477} 489}
478 490
@@ -1212,7 +1224,7 @@ static void gotostat (LexState *ls) {
1212 Labeldesc *lb = findlabel(ls, name); 1224 Labeldesc *lb = findlabel(ls, name);
1213 if (lb == NULL) /* no label? */ 1225 if (lb == NULL) /* no label? */
1214 /* forward jump; will be resolved when the label is declared */ 1226 /* forward jump; will be resolved when the label is declared */
1215 newlabelentry(ls, &ls->dyd->gt, name, line, luaK_jump(fs)); 1227 newgotoentry(ls, name, line, luaK_jump(fs));
1216 else { /* found a label */ 1228 else { /* found a label */
1217 /* backward jump; will be resolved here */ 1229 /* backward jump; will be resolved here */
1218 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */ 1230 if (fs->nactvar > lb->nactvar) /* leaving the scope of some variable? */
@@ -1226,15 +1238,10 @@ static void gotostat (LexState *ls) {
1226/* 1238/*
1227** Break statement. Semantically equivalent to "goto break". 1239** Break statement. Semantically equivalent to "goto break".
1228*/ 1240*/
1229static void breakstat (LexState *ls, int pc) { 1241static void breakstat (LexState *ls) {
1230 FuncState *fs = ls->fs;
1231 int line = ls->linenumber; 1242 int line = ls->linenumber;
1232 BlockCnt *bl = fs->bl;
1233 luaX_next(ls); /* skip break */ 1243 luaX_next(ls); /* skip break */
1234 newlabelentry(ls, &ls->dyd->gt, luaS_newliteral(ls->L, "break"), line, pc); 1244 newgotoentry(ls, luaS_newliteral(ls->L, "break"), line, luaK_jump(ls->fs));
1235 while (bl && !bl->isloop) { bl = bl->previous; }
1236 if (!bl)
1237 luaX_syntaxerror(ls, "no loop to break");
1238} 1245}
1239 1246
1240 1247
@@ -1243,7 +1250,7 @@ static void breakstat (LexState *ls, int pc) {
1243*/ 1250*/
1244static void checkrepeated (LexState *ls, TString *name) { 1251static void checkrepeated (LexState *ls, TString *name) {
1245 Labeldesc *lb = findlabel(ls, name); 1252 Labeldesc *lb = findlabel(ls, name);
1246 if (lb != NULL) { /* already defined? */ 1253 if (unlikely(lb != NULL)) { /* already defined? */
1247 const char *msg = "label '%s' already defined on line %d"; 1254 const char *msg = "label '%s' already defined on line %d";
1248 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line); 1255 msg = luaO_pushfstring(ls->L, msg, getstr(name), lb->line);
1249 luaK_semerror(ls, msg); /* error */ 1256 luaK_semerror(ls, msg); /* error */
@@ -1332,7 +1339,7 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) {
1332 int offset = dest - (pc + 1); 1339 int offset = dest - (pc + 1);
1333 if (back) 1340 if (back)
1334 offset = -offset; 1341 offset = -offset;
1335 if (offset > MAXARG_Bx) 1342 if (unlikely(offset > MAXARG_Bx))
1336 luaX_syntaxerror(fs->ls, "control structure too long"); 1343 luaX_syntaxerror(fs->ls, "control structure too long");
1337 SETARG_Bx(*jmp, offset); 1344 SETARG_Bx(*jmp, offset);
1338} 1345}
@@ -1439,28 +1446,67 @@ static void forstat (LexState *ls, int line) {
1439} 1446}
1440 1447
1441 1448
1449/*
1450** Check whether next instruction is a single jump (a 'break', a 'goto'
1451** to a forward label, or a 'goto' to a backward label with no variable
1452** to close). If so, set the name of the 'label' it is jumping to
1453** ("break" for a 'break') or to where it is jumping to ('target') and
1454** return true. If not a single jump, leave input unchanged, to be
1455** handled as a regular statement.
1456*/
1457static int issinglejump (LexState *ls, TString **label, int *target) {
1458 if (testnext(ls, TK_BREAK)) { /* a break? */
1459 *label = luaS_newliteral(ls->L, "break");
1460 return 1;
1461 }
1462 else if (ls->t.token != TK_GOTO || luaX_lookahead(ls) != TK_NAME)
1463 return 0; /* not a valid goto */
1464 else {
1465 TString *lname = ls->lookahead.seminfo.ts; /* label's id */
1466 Labeldesc *lb = findlabel(ls, lname);
1467 if (lb) { /* a backward jump? */
1468 if (ls->fs->nactvar > lb->nactvar) /* needs to close variables? */
1469 return 0; /* not a single jump; cannot optimize */
1470 *target = lb->pc;
1471 }
1472 else /* jump forward */
1473 *label = lname;
1474 luaX_next(ls); /* skip goto */
1475 luaX_next(ls); /* skip name */
1476 return 1;
1477 }
1478}
1479
1480
1442static void test_then_block (LexState *ls, int *escapelist) { 1481static void test_then_block (LexState *ls, int *escapelist) {
1443 /* test_then_block -> [IF | ELSEIF] cond THEN block */ 1482 /* test_then_block -> [IF | ELSEIF] cond THEN block */
1444 BlockCnt bl; 1483 BlockCnt bl;
1484 int line;
1445 FuncState *fs = ls->fs; 1485 FuncState *fs = ls->fs;
1486 TString *jlb = NULL;
1487 int target = NO_JUMP;
1446 expdesc v; 1488 expdesc v;
1447 int jf; /* instruction to skip 'then' code (if condition is false) */ 1489 int jf; /* instruction to skip 'then' code (if condition is false) */
1448 luaX_next(ls); /* skip IF or ELSEIF */ 1490 luaX_next(ls); /* skip IF or ELSEIF */
1449 expr(ls, &v); /* read condition */ 1491 expr(ls, &v); /* read condition */
1450 checknext(ls, TK_THEN); 1492 checknext(ls, TK_THEN);
1451 if (ls->t.token == TK_BREAK) { 1493 line = ls->linenumber;
1494 if (issinglejump(ls, &jlb, &target)) { /* 'if x then goto' ? */
1452 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ 1495 luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */
1453 enterblock(fs, &bl, 0); /* must enter block before 'goto' */ 1496 enterblock(fs, &bl, 0); /* must enter block before 'goto' */
1454 breakstat(ls, v.t); /* handle break */ 1497 if (jlb != NULL) /* forward jump? */
1498 newgotoentry(ls, jlb, line, v.t); /* will be resolved later */
1499 else /* backward jump */
1500 luaK_patchlist(fs, v.t, target); /* jump directly to 'target' */
1455 while (testnext(ls, ';')) {} /* skip semicolons */ 1501 while (testnext(ls, ';')) {} /* skip semicolons */
1456 if (block_follow(ls, 0)) { /* 'break' is the entire block? */ 1502 if (block_follow(ls, 0)) { /* jump is the entire block? */
1457 leaveblock(fs); 1503 leaveblock(fs);
1458 return; /* and that is it */ 1504 return; /* and that is it */
1459 } 1505 }
1460 else /* must skip over 'then' part if condition is false */ 1506 else /* must skip over 'then' part if condition is false */
1461 jf = luaK_jump(fs); 1507 jf = luaK_jump(fs);
1462 } 1508 }
1463 else { /* regular case (not goto/break) */ 1509 else { /* regular case (not a jump) */
1464 luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ 1510 luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */
1465 enterblock(fs, &bl, 0); 1511 enterblock(fs, &bl, 0);
1466 jf = v.f; 1512 jf = v.f;
@@ -1671,7 +1717,7 @@ static void statement (LexState *ls) {
1671 break; 1717 break;
1672 } 1718 }
1673 case TK_BREAK: { /* stat -> breakstat */ 1719 case TK_BREAK: { /* stat -> breakstat */
1674 breakstat(ls, luaK_jump(ls->fs)); 1720 breakstat(ls);
1675 break; 1721 break;
1676 } 1722 }
1677 case TK_GOTO: { /* stat -> 'goto' NAME */ 1723 case TK_GOTO: { /* stat -> 'goto' NAME */