aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-06-14 13:28:21 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-06-14 13:28:21 -0300
commit04e19712a5d48b84869f9942836ff8314fb0be8e (patch)
tree75aab8dff5bdf07026080eba0189e4025ec3b7ee
parent901d76009346d76996679c02deee708bf225e91e (diff)
downloadlua-04e19712a5d48b84869f9942836ff8314fb0be8e.tar.gz
lua-04e19712a5d48b84869f9942836ff8314fb0be8e.tar.bz2
lua-04e19712a5d48b84869f9942836ff8314fb0be8e.zip
C functions can be tail called, too
A tail call to a C function can have the behavior of a "real" tail call, reusing the stack frame of the caller.
-rw-r--r--ldo.c43
-rw-r--r--lvm.c9
-rw-r--r--testes/coroutine.lua2
-rw-r--r--testes/errors.lua4
4 files changed, 29 insertions, 29 deletions
diff --git a/ldo.c b/ldo.c
index a410461b..38540561 100644
--- a/ldo.c
+++ b/ldo.c
@@ -478,12 +478,31 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
478** (This is done only when no more errors can occur before entering the 478** (This is done only when no more errors can occur before entering the
479** new function, to keep debug information always consistent.) 479** new function, to keep debug information always consistent.)
480*/ 480*/
481static void moveparams (lua_State *L, StkId prevf, StkId func, int narg) { 481static void moveparams (lua_State *L, StkId prevf, StkId func) {
482 int i; 482 int i;
483 narg++; /* function itself will be moved, too */ 483 for (i = 0; func + i < L->top; i++) /* move down function and arguments */
484 for (i = 0; i < narg; i++) /* move down function and arguments */
485 setobjs2s(L, prevf + i, func + i); 484 setobjs2s(L, prevf + i, func + i);
486 L->top = prevf + narg; /* correct top */ 485 L->top = prevf + i; /* correct top */
486}
487
488
489static CallInfo *prepCallInfo (lua_State *L, StkId func, int nresults,
490 int delta1, int mask) {
491 CallInfo *ci;
492 if (delta1) { /* tail call? */
493 ci = L->ci; /* reuse stack frame */
494 ci->func -= delta1 - 1; /* correct 'func' */
495
496 ci->callstatus |= mask | CIST_TAIL;
497 moveparams(L, ci->func, func);
498 }
499 else { /* regular call */
500 ci = L->ci = next_ci(L); /* new frame */
501 ci->func = func;
502 ci->nresults = nresults;
503 ci->callstatus = mask;
504 }
505 return ci;
487} 506}
488 507
489 508
@@ -512,11 +531,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) {
512 int n; /* number of returns */ 531 int n; /* number of returns */
513 CallInfo *ci; 532 CallInfo *ci;
514 checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ 533 checkstackGCp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
515 L->ci = ci = next_ci(L); 534 ci = prepCallInfo(L, func, nresults, delta1, CIST_C);
516 ci->nresults = nresults;
517 ci->callstatus = CIST_C;
518 ci->top = L->top + LUA_MINSTACK; 535 ci->top = L->top + LUA_MINSTACK;
519 ci->func = func;
520 lua_assert(ci->top <= L->stack_last); 536 lua_assert(ci->top <= L->stack_last);
521 if (l_unlikely(L->hookmask & LUA_MASKCALL)) { 537 if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
522 int narg = cast_int(L->top - func) - 1; 538 int narg = cast_int(L->top - func) - 1;
@@ -536,16 +552,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) {
536 int nfixparams = p->numparams; 552 int nfixparams = p->numparams;
537 int fsize = p->maxstacksize; /* frame size */ 553 int fsize = p->maxstacksize; /* frame size */
538 checkstackGCp(L, fsize, func); 554 checkstackGCp(L, fsize, func);
539 if (delta1) { /* tail call? */ 555 ci = prepCallInfo(L, func, nresults, delta1, 0);
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 }
549 ci->u.l.savedpc = p->code; /* starting point */ 556 ci->u.l.savedpc = p->code; /* starting point */
550 ci->top = func + 1 + fsize; 557 ci->top = func + 1 + fsize;
551 for (; narg < nfixparams; narg++) 558 for (; narg < nfixparams; narg++)
diff --git a/lvm.c b/lvm.c
index 485b9caa..62ff70da 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1636,7 +1636,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
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;
1640 goto startfunc; 1639 goto startfunc;
1641 } 1640 }
1642 vmbreak; 1641 vmbreak;
@@ -1655,16 +1654,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1655 lua_assert(L->tbclist < base); /* no pending tbc variables */ 1654 lua_assert(L->tbclist < base); /* no pending tbc variables */
1656 lua_assert(base == ci->func + 1); 1655 lua_assert(base == ci->func + 1);
1657 } 1656 }
1658 if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) { /* Lua function? */ 1657 if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) /* Lua function? */
1659 ci->callstatus |= CIST_TAIL;
1660 goto startfunc; /* execute the callee */ 1658 goto startfunc; /* execute the callee */
1661 }
1662 else { /* C function */ 1659 else { /* C function */
1663 updatetrap(ci); 1660 updatetrap(ci);
1664 updatestack(ci); /* stack may have been relocated */
1665 ci->func -= delta; /* restore 'func' (if vararg) */
1666 luaD_poscall(L, ci, cast_int(L->top - ra)); /* finish caller */
1667 updatetrap(ci); /* 'luaD_poscall' can change hooks */
1668 goto ret; /* caller returns after the tail call */ 1661 goto ret; /* caller returns after the tail call */
1669 } 1662 }
1670 } 1663 }
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index b36b76ea..461e0377 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -205,7 +205,7 @@ do
205 co = coroutine.create(function () return pcall(foo) end) 205 co = coroutine.create(function () return pcall(foo) end)
206 local st1, st2, err = coroutine.resume(co) 206 local st1, st2, err = coroutine.resume(co)
207 assert(st1 and not st2 and err == 43) 207 assert(st1 and not st2 and err == 43)
208 assert(X == 43 and Y.name == "pcall") 208 assert(X == 43 and Y.what == "C")
209 209
210 -- recovering from errors in __close metamethods 210 -- recovering from errors in __close metamethods
211 local track = {} 211 local track = {}
diff --git a/testes/errors.lua b/testes/errors.lua
index a3d0676b..825f37c2 100644
--- a/testes/errors.lua
+++ b/testes/errors.lua
@@ -26,7 +26,7 @@ end
26 26
27local function checkmessage (prog, msg, debug) 27local function checkmessage (prog, msg, debug)
28 local m = doit(prog) 28 local m = doit(prog)
29 if debug then print(m) end 29 if debug then print(m, msg) end
30 assert(string.find(m, msg, 1, true)) 30 assert(string.find(m, msg, 1, true))
31end 31end
32 32
@@ -289,7 +289,7 @@ end]], "global 'insert'")
289 289
290checkmessage([[ -- tail call 290checkmessage([[ -- tail call
291 return math.sin("a") 291 return math.sin("a")
292]], "'sin'") 292]], "sin")
293 293
294checkmessage([[collectgarbage("nooption")]], "invalid option") 294checkmessage([[collectgarbage("nooption")]], "invalid option")
295 295