diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-30 15:04:19 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-30 15:04:19 -0300 |
commit | 2316ec4c24a475e091ec3153a5bd908801a3a109 (patch) | |
tree | ce14172ed7d7dc8874ea282a97d41f4b380f419e /lparser.c | |
parent | a006514ea138a29b6031058d9002b48a572b5dd6 (diff) | |
download | lua-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.c | 84 |
1 files changed, 65 insertions, 19 deletions
@@ -113,7 +113,7 @@ static void checknext (LexState *ls, int c) { | |||
113 | 113 | ||
114 | 114 | ||
115 | static void check_match (LexState *ls, int what, int who, int where) { | 115 | static 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 | ||
396 | static 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 | */ |
473 | static l_noret undefgoto (LexState *ls, Labeldesc *gt) { | 478 | static 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 | */ |
1229 | static void breakstat (LexState *ls, int pc) { | 1241 | static 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 | */ |
1244 | static void checkrepeated (LexState *ls, TString *name) { | 1251 | static 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 | */ | ||
1457 | static 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 | |||
1442 | static void test_then_block (LexState *ls, int *escapelist) { | 1481 | static 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 */ |