diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-06-11 13:41:07 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-06-11 13:41:07 -0300 |
commit | 901d76009346d76996679c02deee708bf225e91e (patch) | |
tree | 51fb561c607c498e5dd229ab26c439645715c49e | |
parent | c0ed74c1e130aa6d80e5ffc7b6e32b433aca1765 (diff) | |
download | lua-901d76009346d76996679c02deee708bf225e91e.tar.gz lua-901d76009346d76996679c02deee708bf225e91e.tar.bz2 lua-901d76009346d76996679c02deee708bf225e91e.zip |
Simpler implementation for tail calls
Tail calls handled by 'luaD_precall', like regular calls, to avoid
code duplication.
-rw-r--r-- | ldo.c | 48 | ||||
-rw-r--r-- | ldo.h | 4 | ||||
-rw-r--r-- | lvm.c | 20 |
3 files changed, 33 insertions, 39 deletions
@@ -474,26 +474,16 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
474 | 474 | ||
475 | 475 | ||
476 | /* | 476 | /* |
477 | ** Prepare a function for a tail call, building its call info on top | 477 | ** In a tail call, move function and parameters to previous call frame. |
478 | ** of the current call info. 'narg1' is the number of arguments plus 1 | 478 | ** (This is done only when no more errors can occur before entering the |
479 | ** (so that it includes the function itself). | 479 | ** new function, to keep debug information always consistent.) |
480 | */ | 480 | */ |
481 | void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { | 481 | static void moveparams (lua_State *L, StkId prevf, StkId func, int narg) { |
482 | Proto *p = clLvalue(s2v(func))->p; | ||
483 | int fsize = p->maxstacksize; /* frame size */ | ||
484 | int nfixparams = p->numparams; | ||
485 | int i; | 482 | int i; |
486 | for (i = 0; i < narg1; i++) /* move down function and arguments */ | 483 | narg++; /* function itself will be moved, too */ |
487 | setobjs2s(L, ci->func + i, func + i); | 484 | for (i = 0; i < narg; i++) /* move down function and arguments */ |
488 | checkstackGC(L, fsize); | 485 | setobjs2s(L, prevf + i, func + i); |
489 | func = ci->func; /* moved-down function */ | 486 | L->top = prevf + narg; /* correct top */ |
490 | for (; narg1 <= nfixparams; narg1++) | ||
491 | setnilvalue(s2v(func + narg1)); /* complete missing arguments */ | ||
492 | ci->top = func + 1 + fsize; /* top for new function */ | ||
493 | lua_assert(ci->top <= L->stack_last); | ||
494 | ci->u.l.savedpc = p->code; /* starting point */ | ||
495 | ci->callstatus |= CIST_TAIL; | ||
496 | L->top = func + narg1; /* set top */ | ||
497 | } | 487 | } |
498 | 488 | ||
499 | 489 | ||
@@ -504,8 +494,12 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { | |||
504 | ** to be executed, if it was a Lua function. Otherwise (a C function) | 494 | ** to be executed, if it was a Lua function. Otherwise (a C function) |
505 | ** returns NULL, with all the results on the stack, starting at the | 495 | ** returns NULL, with all the results on the stack, starting at the |
506 | ** original function position. | 496 | ** original function position. |
497 | ** For regular calls, 'delta1' is 0. For tail calls, 'delta1' is the | ||
498 | ** 'delta' (correction of base for vararg functions) plus 1, so that it | ||
499 | ** cannot be zero. Like 'moveparams', this correction can only be done | ||
500 | ** when no more errors can occur in the call. | ||
507 | */ | 501 | */ |
508 | CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { | 502 | CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) { |
509 | lua_CFunction f; | 503 | lua_CFunction f; |
510 | retry: | 504 | retry: |
511 | switch (ttypetag(s2v(func))) { | 505 | switch (ttypetag(s2v(func))) { |
@@ -542,12 +536,18 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { | |||
542 | int nfixparams = p->numparams; | 536 | int nfixparams = p->numparams; |
543 | int fsize = p->maxstacksize; /* frame size */ | 537 | int fsize = p->maxstacksize; /* frame size */ |
544 | checkstackGCp(L, fsize, func); | 538 | checkstackGCp(L, fsize, func); |
545 | L->ci = ci = next_ci(L); | 539 | if (delta1) { /* tail call? */ |
546 | ci->nresults = nresults; | 540 | ci = L->ci; /* reuse stack frame */ |
541 | ci->func -= delta1 - 1; /* correct 'func' */ | ||
542 | moveparams(L, ci->func, func, narg); | ||
543 | } | ||
544 | else { /* regular call */ | ||
545 | L->ci = ci = next_ci(L); /* new frame */ | ||
546 | ci->func = func; | ||
547 | ci->nresults = nresults; | ||
548 | } | ||
547 | ci->u.l.savedpc = p->code; /* starting point */ | 549 | ci->u.l.savedpc = p->code; /* starting point */ |
548 | ci->top = func + 1 + fsize; | 550 | ci->top = func + 1 + fsize; |
549 | ci->func = func; | ||
550 | L->ci = ci; | ||
551 | for (; narg < nfixparams; narg++) | 551 | for (; narg < nfixparams; narg++) |
552 | setnilvalue(s2v(L->top++)); /* complete missing arguments */ | 552 | setnilvalue(s2v(L->top++)); /* complete missing arguments */ |
553 | lua_assert(ci->top <= L->stack_last); | 553 | lua_assert(ci->top <= L->stack_last); |
@@ -572,7 +572,7 @@ static void ccall (lua_State *L, StkId func, int nResults, int inc) { | |||
572 | L->nCcalls += inc; | 572 | L->nCcalls += inc; |
573 | if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) | 573 | if (l_unlikely(getCcalls(L) >= LUAI_MAXCCALLS)) |
574 | luaE_checkcstack(L); | 574 | luaE_checkcstack(L); |
575 | if ((ci = luaD_precall(L, func, nResults)) != NULL) { /* Lua function? */ | 575 | if ((ci = luaD_precall(L, func, nResults, 0)) != NULL) { /* Lua function? */ |
576 | ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ | 576 | ci->callstatus = CIST_FRESH; /* mark that it is a "fresh" execute */ |
577 | luaV_execute(L, ci); /* call it */ | 577 | luaV_execute(L, ci); /* call it */ |
578 | } | 578 | } |
@@ -58,8 +58,8 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name, | |||
58 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, | 58 | LUAI_FUNC void luaD_hook (lua_State *L, int event, int line, |
59 | int fTransfer, int nTransfer); | 59 | int fTransfer, int nTransfer); |
60 | LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); | 60 | LUAI_FUNC void luaD_hookcall (lua_State *L, CallInfo *ci); |
61 | LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n); | 61 | LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, |
62 | LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); | 62 | int delta1); |
63 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); | 63 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); |
64 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); | 64 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); |
65 | LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); | 65 | LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); |
@@ -1632,11 +1632,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1632 | L->top = ra + b; /* top signals number of arguments */ | 1632 | L->top = ra + b; /* top signals number of arguments */ |
1633 | /* else previous instruction set top */ | 1633 | /* else previous instruction set top */ |
1634 | savepc(L); /* in case of errors */ | 1634 | savepc(L); /* in case of errors */ |
1635 | if ((newci = luaD_precall(L, ra, nresults)) == NULL) | 1635 | if ((newci = luaD_precall(L, ra, nresults, 0)) == NULL) |
1636 | updatetrap(ci); /* C call; nothing else to be done */ | 1636 | updatetrap(ci); /* C call; nothing else to be done */ |
1637 | else { /* Lua call: run function in this same C frame */ | 1637 | else { /* Lua call: run function in this same C frame */ |
1638 | ci = newci; | 1638 | ci = newci; |
1639 | ci->callstatus = 0; /* call re-uses 'luaV_execute' */ | 1639 | ci->callstatus = 0; |
1640 | goto startfunc; | 1640 | goto startfunc; |
1641 | } | 1641 | } |
1642 | vmbreak; | 1642 | vmbreak; |
@@ -1648,21 +1648,18 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1648 | int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; | 1648 | int delta = (nparams1) ? ci->u.l.nextraargs + nparams1 : 0; |
1649 | if (b != 0) | 1649 | if (b != 0) |
1650 | L->top = ra + b; | 1650 | L->top = ra + b; |
1651 | else /* previous instruction set top */ | 1651 | /* else previous instruction set top */ |
1652 | b = cast_int(L->top - ra); | ||
1653 | savepc(ci); /* several calls here can raise errors */ | 1652 | savepc(ci); /* several calls here can raise errors */ |
1654 | if (TESTARG_k(i)) { | 1653 | if (TESTARG_k(i)) { |
1655 | luaF_closeupval(L, base); /* close upvalues from current call */ | 1654 | luaF_closeupval(L, base); /* close upvalues from current call */ |
1656 | lua_assert(L->tbclist < base); /* no pending tbc variables */ | 1655 | lua_assert(L->tbclist < base); /* no pending tbc variables */ |
1657 | lua_assert(base == ci->func + 1); | 1656 | lua_assert(base == ci->func + 1); |
1658 | } | 1657 | } |
1659 | while (!ttisfunction(s2v(ra))) { /* not a function? */ | 1658 | if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) { /* Lua function? */ |
1660 | luaD_tryfuncTM(L, ra); /* try '__call' metamethod */ | 1659 | ci->callstatus |= CIST_TAIL; |
1661 | b++; /* there is now one extra argument */ | 1660 | goto startfunc; /* execute the callee */ |
1662 | checkstackGCp(L, 1, ra); | ||
1663 | } | 1661 | } |
1664 | if (!ttisLclosure(s2v(ra))) { /* C function? */ | 1662 | else { /* C function */ |
1665 | luaD_precall(L, ra, LUA_MULTRET); /* call it */ | ||
1666 | updatetrap(ci); | 1663 | updatetrap(ci); |
1667 | updatestack(ci); /* stack may have been relocated */ | 1664 | updatestack(ci); /* stack may have been relocated */ |
1668 | ci->func -= delta; /* restore 'func' (if vararg) */ | 1665 | ci->func -= delta; /* restore 'func' (if vararg) */ |
@@ -1670,9 +1667,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) { | |||
1670 | updatetrap(ci); /* 'luaD_poscall' can change hooks */ | 1667 | updatetrap(ci); /* 'luaD_poscall' can change hooks */ |
1671 | goto ret; /* caller returns after the tail call */ | 1668 | goto ret; /* caller returns after the tail call */ |
1672 | } | 1669 | } |
1673 | ci->func -= delta; /* restore 'func' (if vararg) */ | ||
1674 | luaD_pretailcall(L, ci, ra, b); /* prepare call frame */ | ||
1675 | goto startfunc; /* execute the callee */ | ||
1676 | } | 1670 | } |
1677 | vmcase(OP_RETURN) { | 1671 | vmcase(OP_RETURN) { |
1678 | int n = GETARG_B(i) - 1; /* number of results */ | 1672 | int n = GETARG_B(i) - 1; /* number of results */ |