Using CIST_CLSRET instead of trick with 'nresults'

The callstatus flag CIST_CLSRET is used in all tests for the
presence of variables to be closed in C functions.
This commit is contained in:
Roberto Ierusalimschy 2024-07-19 17:34:22 -03:00
parent a546138d15
commit f407b3c4a1
4 changed files with 31 additions and 30 deletions

8
lapi.c
View file

@ -207,7 +207,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
} }
newtop = L->top.p + diff; newtop = L->top.p + diff;
if (diff < 0 && L->tbclist.p >= newtop) { if (diff < 0 && L->tbclist.p >= newtop) {
lua_assert(hastocloseCfunc(ci->nresults)); lua_assert(ci->callstatus & CIST_CLSRET);
newtop = luaF_close(L, newtop, CLOSEKTOP, 0); newtop = luaF_close(L, newtop, CLOSEKTOP, 0);
} }
L->top.p = newtop; /* correct top only after closing any upvalue */ L->top.p = newtop; /* correct top only after closing any upvalue */
@ -219,7 +219,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
StkId level; StkId level;
lua_lock(L); lua_lock(L);
level = index2stack(L, idx); level = index2stack(L, idx);
api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level,
"no variable to close at given level"); "no variable to close at given level");
level = luaF_close(L, level, CLOSEKTOP, 0); level = luaF_close(L, level, CLOSEKTOP, 0);
setnilvalue(s2v(level)); setnilvalue(s2v(level));
@ -1287,9 +1287,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) {
nresults = L->ci->nresults; nresults = L->ci->nresults;
api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); api_check(L, L->tbclist.p < o, "given index below or equal a marked one");
luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */
if (!hastocloseCfunc(nresults)) /* function not marked yet? */ L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */
L->ci->nresults = codeNresults(nresults); /* mark it */
lua_assert(hastocloseCfunc(L->ci->nresults));
lua_unlock(L); lua_unlock(L);
} }

16
lapi.h
View file

@ -62,20 +62,4 @@
L->tbclist.p < L->top.p - (n), \ L->tbclist.p < L->top.p - (n), \
"not enough free elements in the stack") "not enough free elements in the stack")
/*
** To reduce the overhead of returning from C functions, the presence of
** to-be-closed variables in these functions is coded in the CallInfo's
** field 'nresults', in a way that functions with no to-be-closed variables
** with zero, one, or "all" wanted results have no overhead. Functions
** with other number of wanted results, as well as functions with
** variables to be closed, have an extra check.
*/
#define hastocloseCfunc(n) ((n) < LUA_MULTRET)
/* Map [-1, inf) (range of 'nresults') into (-inf, -2] */
#define codeNresults(n) (-(n) - 3)
#define decodeNresults(n) (-(n) - 3)
#endif #endif

20
ldo.c
View file

@ -462,22 +462,23 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
StkId firstresult; StkId firstresult;
int i; int i;
switch (wanted) { /* handle typical cases separately */ switch (wanted) { /* handle typical cases separately */
case 0: /* no values needed */ case 0 + 1: /* no values needed */
L->top.p = res; L->top.p = res;
return; return;
case 1: /* one value needed */ case 1 + 1: /* one value needed */
if (nres == 0) /* no results? */ if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */ setnilvalue(s2v(res)); /* adjust with nil */
else /* at least one result */ else /* at least one result */
setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ setobjs2s(L, res, L->top.p - nres); /* move it to proper place */
L->top.p = res + 1; L->top.p = res + 1;
return; return;
case LUA_MULTRET: case LUA_MULTRET + 1:
wanted = nres; /* we want all results */ wanted = nres; /* we want all results */
break; break;
default: /* two/more results and/or to-be-closed variables */ default: /* two/more results and/or to-be-closed variables */
if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ if (!(wanted & CIST_CLSRET))
L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ wanted--;
else { /* to-be-closed variables? */
L->ci->u2.nres = nres; L->ci->u2.nres = nres;
res = luaF_close(L, res, CLOSEKTOP, 1); res = luaF_close(L, res, CLOSEKTOP, 1);
L->ci->callstatus &= ~CIST_CLSRET; L->ci->callstatus &= ~CIST_CLSRET;
@ -486,7 +487,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
rethook(L, L->ci, nres); rethook(L, L->ci, nres);
res = restorestack(L, savedres); /* hook can move stack */ res = restorestack(L, savedres); /* hook can move stack */
} }
wanted = decodeNresults(wanted); wanted = (wanted & ~CIST_CLSRET) - 1;
if (wanted == LUA_MULTRET) if (wanted == LUA_MULTRET)
wanted = nres; /* we want all results */ wanted = nres; /* we want all results */
} }
@ -511,8 +512,10 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) {
** that. ** that.
*/ */
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
int wanted = ci->nresults; int wanted = ci->nresults + 1;
if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) if (ci->callstatus & CIST_CLSRET)
wanted |= CIST_CLSRET; /* don't check hook in this case */
else if (l_unlikely(L->hookmask))
rethook(L, ci, nres); rethook(L, ci, nres);
/* move results to proper place */ /* move results to proper place */
moveresults(L, ci->func.p, nres, wanted); moveresults(L, ci->func.p, nres, wanted);
@ -736,7 +739,6 @@ static int finishpcallk (lua_State *L, CallInfo *ci) {
static void finishCcall (lua_State *L, CallInfo *ci) { static void finishCcall (lua_State *L, CallInfo *ci) {
int n; /* actual number of results from C function */ int n; /* actual number of results from C function */
if (ci->callstatus & CIST_CLSRET) { /* was returning? */ if (ci->callstatus & CIST_CLSRET) { /* was returning? */
lua_assert(hastocloseCfunc(ci->nresults));
n = ci->u2.nres; /* just redo 'luaD_poscall' */ n = ci->u2.nres; /* just redo 'luaD_poscall' */
/* don't need to reset CIST_CLSRET, as it will be set again anyway */ /* don't need to reset CIST_CLSRET, as it will be set again anyway */
} }

View file

@ -165,6 +165,23 @@ do -- test returning more results than fit in the caller stack
end end
do -- testing multipe returns
local function foo (n)
if n > 0 then return n, foo(n - 1) end
end
local t = {T.testC("call 1 10; return 10", foo, 20)}
assert(t[1] == 20 and t[10] == 11 and t[11] == nil)
local t = table.pack(T.testC("call 1 10; return 10", foo, 2))
assert(t[1] == 2 and t[2] == 1 and t[3] == nil and t.n == 10)
local t = {T.testC([[
checkstack 300 "error"; call 1 250; return 250]], foo, 250)}
assert(t[1] == 250 and t[250] == 1 and t[251] == nil)
end
-- testing globals -- testing globals
_G.AA = 14; _G.BB = "a31" _G.AA = 14; _G.BB = "a31"
local a = {T.testC[[ local a = {T.testC[[