Przeglądaj źródła

more direct implementation for tail calls.

Roberto Ierusalimschy 7 lat temu
rodzic
commit
14c3aa12b5
3 zmienionych plików z 57 dodań i 36 usunięć
  1. 37 10
      ldo.c
  2. 3 1
      ldo.h
  3. 17 25
      lvm.c

+ 37 - 10
ldo.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: ldo.c,v 2.171 2017/11/13 12:26:30 roberto Exp roberto $
+** $Id: ldo.c,v 2.172 2017/11/13 15:36:52 roberto Exp roberto $
 ** Stack and Call structure of Lua
 ** Stack and Call structure of Lua
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -280,14 +280,13 @@ void luaD_hook (lua_State *L, int event, int line) {
 }
 }
 
 
 
 
-static void callhook (lua_State *L, CallInfo *ci) {
+static void callhook (lua_State *L, CallInfo *ci, int istail) {
   int hook;
   int hook;
   ci->u.l.trap = 1;
   ci->u.l.trap = 1;
   if (!(L->hookmask & LUA_MASKCALL))
   if (!(L->hookmask & LUA_MASKCALL))
     return;  /* some other hook */
     return;  /* some other hook */
   ci->u.l.savedpc++;  /* hooks assume 'pc' is already incremented */
   ci->u.l.savedpc++;  /* hooks assume 'pc' is already incremented */
-  if (isLua(ci->previous) &&
-      GET_OPCODE(*(ci->previous->u.l.savedpc - 1)) == OP_TAILCALL) {
+  if (istail) {
     ci->callstatus |= CIST_TAIL;
     ci->callstatus |= CIST_TAIL;
     hook = LUA_HOOKTAILCALL;
     hook = LUA_HOOKTAILCALL;
   }
   }
@@ -303,16 +302,18 @@ static void callhook (lua_State *L, CallInfo *ci) {
 ** it in stack below original 'func' so that 'luaD_precall' can call
 ** it in stack below original 'func' so that 'luaD_precall' can call
 ** it. Raise an error if __call metafield is not a function.
 ** it. Raise an error if __call metafield is not a function.
 */
 */
-static void tryfuncTM (lua_State *L, StkId func) {
+StkId luaD_tryfuncTM (lua_State *L, StkId func) {
   const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
   const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CALL);
   StkId p;
   StkId p;
   if (!ttisfunction(tm))
   if (!ttisfunction(tm))
     luaG_typeerror(L, s2v(func), "call");
     luaG_typeerror(L, s2v(func), "call");
   /* Open a hole inside the stack at 'func' */
   /* Open a hole inside the stack at 'func' */
+  checkstackp(L, 1, func);  /* ensure space for metamethod */
   for (p = L->top; p > func; p--)
   for (p = L->top; p > func; p--)
     setobjs2s(L, p, p-1);
     setobjs2s(L, p, p-1);
-  L->top++;  /* slot ensured by caller */
-  setobj2s(L, func, tm);  /* tag method is the new function to be called */
+  L->top++;
+  setobj2s(L, func, tm);  /* metamethod is the new function to be called */
+  return func;
 }
 }
 
 
 
 
@@ -388,6 +389,33 @@ int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) {
 #define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
 #define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
 
 
 
 
+/*
+** Prepare a function for a tail call, building its call info on top
+** of the current call info. 'n' is the number of arguments plus 1
+** (so that it includes the function itself).
+*/
+void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n) {
+  Proto *p = clLvalue(s2v(func))->p;
+  int fsize = p->maxstacksize;  /* frame size */
+  int i;
+  for (i = 0; i < n; i++)  /* move down function and arguments */
+    setobjs2s(L, ci->func + i, func + i);
+  checkstackp(L, fsize, func);
+  for (; i < p->numparams - p->is_vararg; i++)
+    setnilvalue(s2v(ci->func + i));  /* complete missing parameters */
+  if (p->is_vararg) {
+    L->top -= (func - ci->func);  /* move down top */
+    luaT_adjustvarargs(L, p, n - 1);
+  }
+  L->top = ci->top = ci->func + 1 + fsize;  /* top for new function */
+  lua_assert(ci->top <= L->stack_last);
+  ci->u.l.savedpc = p->code;  /* starting point */
+  ci->callstatus |= CIST_TAIL;
+  if (L->hookmask)
+    callhook(L, ci, 1);
+}
+
+
 /*
 /*
 ** Prepares a function call: checks the stack, creates a new CallInfo
 ** Prepares a function call: checks the stack, creates a new CallInfo
 ** entry, fills in the relevant information, calls hook if needed.
 ** entry, fills in the relevant information, calls hook if needed.
@@ -440,12 +468,11 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
       ci->u.l.savedpc = p->code;  /* starting point */
       ci->u.l.savedpc = p->code;  /* starting point */
       ci->callstatus = CIST_LUA;
       ci->callstatus = CIST_LUA;
       if (L->hookmask)
       if (L->hookmask)
-        callhook(L, ci);
+        callhook(L, ci, 0);
       return 0;
       return 0;
     }
     }
     default: {  /* not a function */
     default: {  /* not a function */
-      checkstackp(L, 1, func);  /* ensure space for metamethod */
-      tryfuncTM(L, func);  /* try to get '__call' metamethod */
+      func = luaD_tryfuncTM(L, func);  /* try to get '__call' metamethod */
       return luaD_precall(L, func, nresults);  /* now it must be a function */
       return luaD_precall(L, func, nresults);  /* now it must be a function */
     }
     }
   }
   }

+ 3 - 1
ldo.h

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: ldo.h,v 2.31 2017/06/29 15:06:44 roberto Exp $
+** $Id: ldo.h,v 2.33 2017/11/07 13:25:26 roberto Exp roberto $
 ** Stack and Call structure of Lua
 ** Stack and Call structure of Lua
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -48,8 +48,10 @@ LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
                                                   const char *mode);
                                                   const char *mode);
 LUAI_FUNC void luaD_hook (lua_State *L, int event, int line);
 LUAI_FUNC void luaD_hook (lua_State *L, int event, int line);
 LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
 LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
+LUAI_FUNC void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int n);
 LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
 LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
 LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
 LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
+LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
 LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
 LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
                                         ptrdiff_t oldtop, ptrdiff_t ef);
                                         ptrdiff_t oldtop, ptrdiff_t ef);
 LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult,
 LUAI_FUNC int luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult,

+ 17 - 25
lvm.c

@@ -1,5 +1,5 @@
 /*
 /*
-** $Id: lvm.c,v 2.311 2017/11/16 12:59:14 roberto Exp roberto $
+** $Id: lvm.c,v 2.312 2017/11/20 12:57:39 roberto Exp roberto $
 ** Lua virtual machine
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 ** See Copyright Notice in lua.h
 */
 */
@@ -1429,31 +1429,23 @@ void luaV_execute (lua_State *L) {
         vmbreak;
         vmbreak;
       }
       }
       vmcase(OP_TAILCALL) {
       vmcase(OP_TAILCALL) {
-        int b = GETARG_B(i);
-        if (b != 0) L->top = ra+b;  /* else previous instruction set top */
+        int b = GETARG_B(i);  /* number of arguments + 1 (function) */
+        if (b != 0)
+          L->top = ra + b;
+        else  /* previous instruction set top */
+          b = L->top - ra;
         lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
         lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
-        savepc(L);
-        if (luaD_precall(L, ra, LUA_MULTRET))  /* C function? */
-          updatetrap(ci);
-        else {
-          /* tail call: put called frame (n) in place of caller one (o) */
-          CallInfo *nci = L->ci;  /* called frame (new) */
-          CallInfo *oci = nci->previous;  /* caller frame (old) */
-          StkId nfunc = nci->func;  /* called function */
-          StkId ofunc = oci->func;  /* caller function */
-          /* last stack slot filled by 'precall' */
-          StkId lim = nfunc + 1 + getproto(s2v(nfunc))->numparams;
-          int aux;
-          /* close all upvalues from previous call */
-          if (cl->p->sizep > 0) luaF_close(L, ofunc + 1);
-          /* move new frame into old one */
-          for (aux = 0; nfunc + aux < lim; aux++)
-            setobjs2s(L, ofunc + aux, nfunc + aux);
-          oci->top = L->top = ofunc + (L->top - nfunc);  /* correct top */
-          oci->u.l.savedpc = nci->u.l.savedpc;
-          oci->callstatus |= CIST_TAIL;  /* function was tail called */
-          ci = L->ci = oci;  /* remove new frame */
-          lua_assert(L->top == ofunc + 1 + getproto(s2v(ofunc))->maxstacksize);
+        if (!ttisfunction(s2v(ra))) {  /* not a function? */
+          /* try to get '__call' metamethod */
+          Protect(ra = luaD_tryfuncTM(L, ra));
+          b++;  /* there is now one extra argument */
+        }
+        if (!ttisLclosure(s2v(ra)))  /* C function? */
+          Protect(luaD_precall(L, ra, LUA_MULTRET));  /* call it */
+        else {  /* tail call */
+          if (cl->p->sizep > 0) /* close upvalues from previous call */
+            luaF_close(L, ci->func + 1);
+          luaD_pretailcall(L, ci, ra, b);  /* prepare call frame */
           goto newframe;  /* restart luaV_execute over new Lua function */
           goto newframe;  /* restart luaV_execute over new Lua function */
         }
         }
         vmbreak;
         vmbreak;