2
0
Эх сурвалжийг харах

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.
Roberto Ierusalimschy 4 жил өмнө
parent
commit
04e19712a5
4 өөрчлөгдсөн 29 нэмэгдсэн , 29 устгасан
  1. 25 18
      ldo.c
  2. 1 8
      lvm.c
  3. 1 1
      testes/coroutine.lua
  4. 2 2
      testes/errors.lua

+ 25 - 18
ldo.c

@@ -478,12 +478,31 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
 ** (This is done only when no more errors can occur before entering the
 ** new function, to keep debug information always consistent.)
 */
-static void moveparams (lua_State *L, StkId prevf, StkId func, int narg) {
+static void moveparams (lua_State *L, StkId prevf, StkId func) {
   int i;
-  narg++;  /* function itself will be moved, too */
-  for (i = 0; i < narg; i++)  /* move down function and arguments */
+  for (i = 0; func + i < L->top; i++)  /* move down function and arguments */
     setobjs2s(L, prevf + i, func + i);
-  L->top = prevf + narg;  /* correct top */
+  L->top = prevf + i;  /* correct top */
+}
+
+
+static CallInfo *prepCallInfo (lua_State *L, StkId func, int nresults,
+                                             int delta1, int mask) {
+  CallInfo *ci;
+  if (delta1) {  /* tail call? */
+    ci = L->ci;  /* reuse stack frame */
+    ci->func -= delta1 - 1;  /* correct 'func' */
+
+    ci->callstatus |= mask | CIST_TAIL;
+    moveparams(L, ci->func, func);
+  }
+  else {  /* regular call */
+    ci = L->ci = next_ci(L);  /* new frame */
+    ci->func = func;
+    ci->nresults = nresults;
+    ci->callstatus = mask;
+  }
+  return ci;
 }
 
 
@@ -512,11 +531,8 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) {
       int n;  /* number of returns */
       CallInfo *ci;
       checkstackGCp(L, LUA_MINSTACK, func);  /* ensure minimum stack size */
-      L->ci = ci = next_ci(L);
-      ci->nresults = nresults;
-      ci->callstatus = CIST_C;
+      ci = prepCallInfo(L, func, nresults, delta1, CIST_C);
       ci->top = L->top + LUA_MINSTACK;
-      ci->func = func;
       lua_assert(ci->top <= L->stack_last);
       if (l_unlikely(L->hookmask & LUA_MASKCALL)) {
         int narg = cast_int(L->top - func) - 1;
@@ -536,16 +552,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults, int delta1) {
       int nfixparams = p->numparams;
       int fsize = p->maxstacksize;  /* frame size */
       checkstackGCp(L, fsize, func);
-      if (delta1) {  /* tail call? */
-        ci = L->ci;  /* reuse stack frame */
-        ci->func -= delta1 - 1;  /* correct 'func' */
-        moveparams(L, ci->func, func, narg);
-      }
-      else {  /* regular call */
-        L->ci = ci = next_ci(L);  /* new frame */
-        ci->func = func;
-        ci->nresults = nresults;
-      }
+      ci = prepCallInfo(L, func, nresults, delta1, 0);
       ci->u.l.savedpc = p->code;  /* starting point */
       ci->top = func + 1 + fsize;
       for (; narg < nfixparams; narg++)

+ 1 - 8
lvm.c

@@ -1636,7 +1636,6 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
           updatetrap(ci);  /* C call; nothing else to be done */
         else {  /* Lua call: run function in this same C frame */
           ci = newci;
-          ci->callstatus = 0;
           goto startfunc;
         }
         vmbreak;
@@ -1655,16 +1654,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
           lua_assert(L->tbclist < base);  /* no pending tbc variables */
           lua_assert(base == ci->func + 1);
         }
-        if (luaD_precall(L, ra, LUA_MULTRET, delta + 1)) {  /* Lua function? */
-          ci->callstatus |= CIST_TAIL;
+        if (luaD_precall(L, ra, LUA_MULTRET, delta + 1))  /* Lua function? */
           goto startfunc;  /* execute the callee */
-        }
         else {  /* C function */
           updatetrap(ci);
-          updatestack(ci);  /* stack may have been relocated */
-          ci->func -= delta;  /* restore 'func' (if vararg) */
-          luaD_poscall(L, ci, cast_int(L->top - ra));  /* finish caller */
-          updatetrap(ci);  /* 'luaD_poscall' can change hooks */
           goto ret;  /* caller returns after the tail call */
         }
       }

+ 1 - 1
testes/coroutine.lua

@@ -205,7 +205,7 @@ do
   co = coroutine.create(function () return pcall(foo) end)
   local st1, st2, err = coroutine.resume(co)
   assert(st1 and not st2 and err == 43)
-  assert(X == 43 and Y.name == "pcall")
+  assert(X == 43 and Y.what == "C")
 
   -- recovering from errors in __close metamethods
   local track = {}

+ 2 - 2
testes/errors.lua

@@ -26,7 +26,7 @@ end
 
 local function checkmessage (prog, msg, debug)
   local m = doit(prog)
-  if debug then print(m) end
+  if debug then print(m, msg) end
   assert(string.find(m, msg, 1, true))
 end
 
@@ -289,7 +289,7 @@ end]], "global 'insert'")
 
 checkmessage([[  -- tail call
   return math.sin("a")
-]], "'sin'")
+]], "sin")
 
 checkmessage([[collectgarbage("nooption")]], "invalid option")