diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-10-29 13:41:24 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-10-29 13:41:24 -0300 |
commit | 1fce5bea817de50e055a84c153a975f25bfcf493 (patch) | |
tree | 294645bf9a85557026d8c99d74c2eac50a74302e /ldo.c | |
parent | 3699446aaf5c7a07af028b1ae43cf52d2d4dda59 (diff) | |
download | lua-1fce5bea817de50e055a84c153a975f25bfcf493.tar.gz lua-1fce5bea817de50e055a84c153a975f25bfcf493.tar.bz2 lua-1fce5bea817de50e055a84c153a975f25bfcf493.zip |
More uniform implementation for tail calls
'luaD_pretailcall' mimics 'luaD_precall', handling call metamethods
and calling C functions directly. That makes the code in the
interpreter loop simpler.
This commit also goes back to emulating the tail call in 'luaD_precall'
with a goto, as C compilers may not do proper tail calls and the C
stack can overflow much sooner than the Lua stack (which grows as the
metamethod is added to it).
Diffstat (limited to 'ldo.c')
-rw-r--r-- | ldo.c | 81 |
1 files changed, 52 insertions, 29 deletions
@@ -475,30 +475,6 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
475 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) | 475 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) |
476 | 476 | ||
477 | 477 | ||
478 | /* | ||
479 | ** Prepare a function for a tail call, building its call info on top | ||
480 | ** of the current call info. 'narg1' is the number of arguments plus 1 | ||
481 | ** (so that it includes the function itself). | ||
482 | */ | ||
483 | void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) { | ||
484 | Proto *p = clLvalue(s2v(func))->p; | ||
485 | int fsize = p->maxstacksize; /* frame size */ | ||
486 | int nfixparams = p->numparams; | ||
487 | int i; | ||
488 | for (i = 0; i < narg1; i++) /* move down function and arguments */ | ||
489 | setobjs2s(L, ci->func + i, func + i); | ||
490 | checkstackGC(L, fsize); | ||
491 | func = ci->func; /* moved-down function */ | ||
492 | for (; narg1 <= nfixparams; narg1++) | ||
493 | setnilvalue(s2v(func + narg1)); /* complete missing arguments */ | ||
494 | ci->top = func + 1 + fsize; /* top for new function */ | ||
495 | lua_assert(ci->top <= L->stack_last); | ||
496 | ci->u.l.savedpc = p->code; /* starting point */ | ||
497 | ci->callstatus |= CIST_TAIL; | ||
498 | L->top = func + narg1; /* set top */ | ||
499 | } | ||
500 | |||
501 | |||
502 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, | 478 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, |
503 | int mask, StkId top) { | 479 | int mask, StkId top) { |
504 | CallInfo *ci = L->ci = next_ci(L); /* new frame */ | 480 | CallInfo *ci = L->ci = next_ci(L); /* new frame */ |
@@ -513,7 +489,7 @@ l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, | |||
513 | /* | 489 | /* |
514 | ** precall for C functions | 490 | ** precall for C functions |
515 | */ | 491 | */ |
516 | l_sinline CallInfo *precallC (lua_State *L, StkId func, int nresults, | 492 | l_sinline int precallC (lua_State *L, StkId func, int nresults, |
517 | lua_CFunction f) { | 493 | lua_CFunction f) { |
518 | int n; /* number of returns */ | 494 | int n; /* number of returns */ |
519 | CallInfo *ci; | 495 | CallInfo *ci; |
@@ -530,7 +506,50 @@ l_sinline CallInfo *precallC (lua_State *L, StkId func, int nresults, | |||
530 | lua_lock(L); | 506 | lua_lock(L); |
531 | api_checknelems(L, n); | 507 | api_checknelems(L, n); |
532 | luaD_poscall(L, ci, n); | 508 | luaD_poscall(L, ci, n); |
533 | return NULL; | 509 | return n; |
510 | } | ||
511 | |||
512 | |||
513 | /* | ||
514 | ** Prepare a function for a tail call, building its call info on top | ||
515 | ** of the current call info. 'narg1' is the number of arguments plus 1 | ||
516 | ** (so that it includes the function itself). Return the number of | ||
517 | ** results, if it was a C function, or -1 for a Lua function. | ||
518 | */ | ||
519 | int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, | ||
520 | int narg1, int delta) { | ||
521 | retry: | ||
522 | switch (ttypetag(s2v(func))) { | ||
523 | case LUA_VCCL: /* C closure */ | ||
524 | return precallC(L, func, LUA_MULTRET, clCvalue(s2v(func))->f); | ||
525 | case LUA_VLCF: /* light C function */ | ||
526 | return precallC(L, func, LUA_MULTRET, fvalue(s2v(func))); | ||
527 | case LUA_VLCL: { /* Lua function */ | ||
528 | Proto *p = clLvalue(s2v(func))->p; | ||
529 | int fsize = p->maxstacksize; /* frame size */ | ||
530 | int nfixparams = p->numparams; | ||
531 | int i; | ||
532 | ci->func -= delta; /* restore 'func' (if vararg) */ | ||
533 | for (i = 0; i < narg1; i++) /* move down function and arguments */ | ||
534 | setobjs2s(L, ci->func + i, func + i); | ||
535 | checkstackGC(L, fsize); | ||
536 | func = ci->func; /* moved-down function */ | ||
537 | for (; narg1 <= nfixparams; narg1++) | ||
538 | setnilvalue(s2v(func + narg1)); /* complete missing arguments */ | ||
539 | ci->top = func + 1 + fsize; /* top for new function */ | ||
540 | lua_assert(ci->top <= L->stack_last); | ||
541 | ci->u.l.savedpc = p->code; /* starting point */ | ||
542 | ci->callstatus |= CIST_TAIL; | ||
543 | L->top = func + narg1; /* set top */ | ||
544 | return -1; | ||
545 | } | ||
546 | default: { /* not a function */ | ||
547 | func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ | ||
548 | /* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */ | ||
549 | narg1++; | ||
550 | goto retry; /* try again */ | ||
551 | } | ||
552 | } | ||
534 | } | 553 | } |
535 | 554 | ||
536 | 555 | ||
@@ -543,11 +562,14 @@ l_sinline CallInfo *precallC (lua_State *L, StkId func, int nresults, | |||
543 | ** original function position. | 562 | ** original function position. |
544 | */ | 563 | */ |
545 | CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { | 564 | CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { |
565 | retry: | ||
546 | switch (ttypetag(s2v(func))) { | 566 | switch (ttypetag(s2v(func))) { |
547 | case LUA_VCCL: /* C closure */ | 567 | case LUA_VCCL: /* C closure */ |
548 | return precallC(L, func, nresults, clCvalue(s2v(func))->f); | 568 | precallC(L, func, nresults, clCvalue(s2v(func))->f); |
569 | return NULL; | ||
549 | case LUA_VLCF: /* light C function */ | 570 | case LUA_VLCF: /* light C function */ |
550 | return precallC(L, func, nresults, fvalue(s2v(func))); | 571 | precallC(L, func, nresults, fvalue(s2v(func))); |
572 | return NULL; | ||
551 | case LUA_VLCL: { /* Lua function */ | 573 | case LUA_VLCL: { /* Lua function */ |
552 | CallInfo *ci; | 574 | CallInfo *ci; |
553 | Proto *p = clLvalue(s2v(func))->p; | 575 | Proto *p = clLvalue(s2v(func))->p; |
@@ -564,7 +586,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) { | |||
564 | } | 586 | } |
565 | default: { /* not a function */ | 587 | default: { /* not a function */ |
566 | func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ | 588 | func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */ |
567 | return luaD_precall(L, func, nresults); /* try again with metamethod */ | 589 | /* return luaD_precall(L, func, nresults); */ |
590 | goto retry; /* try again with metamethod */ | ||
568 | } | 591 | } |
569 | } | 592 | } |
570 | } | 593 | } |