1
0
mirror of https://github.com/lua/lua.git synced 2025-01-14 05:43:00 +08:00

vararg back to '...' (but with another implementation)

new implementation should have zero overhead for non-vararg functions
This commit is contained in:
Roberto Ierusalimschy 2018-02-09 13:16:06 -02:00
parent 4e0de3a43c
commit b1379936cf
11 changed files with 136 additions and 113 deletions

26
lcode.c
View File

@ -1,5 +1,5 @@
/*
** $Id: lcode.c,v 2.151 2018/01/27 16:56:33 roberto Exp roberto $
** $Id: lcode.c,v 2.152 2018/01/28 15:13:26 roberto Exp roberto $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
@ -31,7 +31,7 @@
/* Maximum number of registers in a Lua function (must fit in 8 bits) */
#define MAXREGS 255
#define MAXREGS 254
#define hasjumps(e) ((e)->t != (e)->f)
@ -157,17 +157,17 @@ int luaK_jump (FuncState *fs) {
** Code a 'return' instruction
*/
void luaK_ret (FuncState *fs, int first, int nret) {
switch (nret) {
case 0:
luaK_codeABC(fs, OP_RETURN0, 0, 0, 0);
break;
case 1:
luaK_codeABC(fs, OP_RETURN1, first, 0, 0);
break;
default:
luaK_codeABC(fs, OP_RETURN, first, nret + 1, 0);
break;
OpCode op;
if (fs->f->is_vararg)
op = OP_RETVARARG;
else {
switch (nret) {
case 0: op = OP_RETURN0; break;
case 1: op = OP_RETURN1; break;
default: op = OP_RETURN; break;
}
}
luaK_codeABC(fs, op, first, nret + 1, fs->f->numparams);
}
@ -1647,7 +1647,7 @@ void luaK_finish (FuncState *fs) {
lua_assert(i == 0 || isOT(*(pc - 1)) == isIT(*pc));
switch (GET_OPCODE(*pc)) {
case OP_RETURN: case OP_RETURN0: case OP_RETURN1:
case OP_TAILCALL: {
case OP_RETVARARG: case OP_TAILCALL: {
if (p->sizep > 0)
SETARG_k(*pc, 1); /* signal that they must close upvalues */
break;

View File

@ -1,5 +1,5 @@
/*
** $Id: ldebug.c,v 2.152 2018/01/10 12:02:35 roberto Exp roberto $
** $Id: ldebug.c,v 2.153 2018/02/06 19:16:56 roberto Exp roberto $
** Debug Interface
** See Copyright Notice in lua.h
*/
@ -187,12 +187,28 @@ static const char *upvalname (Proto *p, int uv) {
}
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
if (clLvalue(s2v(ci->func))->p->is_vararg) {
int nextra = ci->u.l.nextraargs;
if (n <= nextra) {
*pos = ci->func - nextra + (n - 1);
return "(*vararg)"; /* generic name for any vararg */
}
}
return NULL; /* no such vararg */
}
static const char *findlocal (lua_State *L, CallInfo *ci, int n,
StkId *pos) {
StkId base = ci->func + 1;
const char *name = (isLua(ci))
? luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci))
: NULL;
const char *name = NULL;
if (isLua(ci)) {
if (n < 0) /* access to vararg values? */
return findvararg(ci, -n, pos);
else
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
}
if (name == NULL) { /* no 'standard' name? */
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
@ -324,7 +340,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
}
else {
ar->isvararg = f->l.p->is_vararg;
ar->nparams = f->l.p->numparams + f->l.p->is_vararg;
ar->nparams = f->l.p->numparams;
}
break;
}

37
ldo.c
View File

@ -1,5 +1,5 @@
/*
** $Id: ldo.c,v 2.191 2018/02/07 15:18:04 roberto Exp roberto $
** $Id: ldo.c,v 2.192 2018/02/07 15:55:18 roberto Exp roberto $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@ -310,7 +310,7 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) {
}
static void rethook (lua_State *L, CallInfo *ci) {
void luaD_rethook (lua_State *L, CallInfo *ci) {
if (isLuacode(ci))
L->top = ci->top; /* prepare top */
if (L->hookmask & LUA_MASKRET) /* is return hook on? */
@ -343,8 +343,8 @@ void luaD_tryfuncTM (lua_State *L, StkId func) {
** expressions, multiple results for tail calls/single parameters)
** separated.
*/
static void moveresults (lua_State *L, StkId firstResult, StkId res,
int nres, int wanted) {
void luaD_moveresults (lua_State *L, StkId firstResult, StkId res,
int nres, int wanted) {
switch (wanted) { /* handle typical cases separately */
case 0: break; /* nothing to move */
case 1: { /* one result needed */
@ -382,27 +382,22 @@ static void moveresults (lua_State *L, StkId firstResult, StkId res,
/*
** Finishes a function call: calls hook if necessary, removes CallInfo,
** moves current number of results to proper place; returns 0 iff call
** wanted multiple (variable number of) results.
** moves current number of results to proper place.
*/
void luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult, int nres) {
if (L->hookmask) {
ptrdiff_t fr = savestack(L, firstResult); /* hook may change stack */
rethook(L, ci);
luaD_rethook(L, ci);
firstResult = restorestack(L, fr);
}
L->ci = ci->previous; /* back to caller */
/* move results to proper place */
moveresults(L, firstResult, ci->func, nres, ci->nresults);
luaD_moveresults(L, firstResult, ci->func, nres, ci->nresults);
}
#define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L))
#define checkstackGC(L,fsize) \
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
#define next_ci(L) (L->ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)))
/*
@ -438,8 +433,6 @@ void luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func, int narg1) {
void luaD_call (lua_State *L, StkId func, int nresults) {
lua_CFunction f;
TValue *funcv = s2v(func);
CallInfo *ci = next_ci(L);
ci->nresults = nresults;
switch (ttype(funcv)) {
case LUA_TCCL: /* C closure */
f = clCvalue(funcv)->f;
@ -448,12 +441,14 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
f = fvalue(funcv);
Cfunc: {
int n; /* number of returns */
CallInfo *ci;
checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */
ci = next_ci(L);
ci->nresults = nresults;
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
lua_assert(ci->top <= L->stack_last);
L->ci = ci; /* now 'enter' new function */
if (L->hookmask & LUA_MASKCALL)
luaD_hook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
@ -464,18 +459,20 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
break;
}
case LUA_TLCL: { /* Lua function */
CallInfo *ci;
Proto *p = clLvalue(funcv)->p;
int narg = cast_int(L->top - func) - 1; /* number of real arguments */
int nfixparams = p->numparams;
int fsize = p->maxstacksize; /* frame size */
ci->u.l.savedpc = p->code; /* starting point */
checkstackp(L, fsize, func);
for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */
ci = next_ci(L);
ci->nresults = nresults;
ci->u.l.savedpc = p->code; /* starting point */
ci->callstatus = 0;
ci->top = func + 1 + fsize;
ci->func = func;
L->ci = ci; /* now 'enter' new function */
for (; narg < nfixparams; narg++)
setnilvalue(s2v(L->top++)); /* complete missing arguments */
lua_assert(ci->top <= L->stack_last);
luaV_execute(L, ci); /* run the function */
break;

10
ldo.h
View File

@ -1,5 +1,5 @@
/*
** $Id: ldo.h,v 2.39 2018/01/10 19:19:27 roberto Exp roberto $
** $Id: ldo.h,v 2.40 2018/02/06 19:16:56 roberto Exp roberto $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
@ -42,6 +42,11 @@
p = restorestack(L, t__)) /* 'pos' part: restore 'p' */
/* macro to check stack size and GC */
#define checkstackGC(L,fsize) \
luaD_checkstackaux(L, (fsize), (void)0, luaC_checkGC(L))
/* type of protected functions, to be ran by 'runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
@ -57,7 +62,10 @@ LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, StkId firstResult,
int nres);
LUAI_FUNC void luaD_rethook (lua_State *L, CallInfo *ci);
LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror);
LUAI_FUNC void luaD_moveresults (lua_State *L, StkId firstResult, StkId res,
int nres, int wanted);
LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror);
LUAI_FUNC void luaD_shrinkstack (lua_State *L);
LUAI_FUNC void luaD_inctop (lua_State *L);

View File

@ -1,5 +1,5 @@
/*
** $Id: lopcodes.c,v 1.75 2017/12/22 14:16:46 roberto Exp roberto $
** $Id: lopcodes.c,v 1.76 2018/02/07 15:18:04 roberto Exp roberto $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
@ -80,6 +80,7 @@ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = {
"CALL",
"TAILCALL",
"RETURN",
"RETVARARG",
"RETURN0",
"RETURN1",
"FORLOOP1",
@ -161,6 +162,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
,opmode(1, 1, 0, 1, iABC) /* OP_CALL */
,opmode(1, 1, 0, 1, iABC) /* OP_TAILCALL */
,opmode(0, 1, 0, 0, iABC) /* OP_RETURN */
,opmode(0, 1, 0, 0, iABC) /* OP_RETVARARG */
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN0 */
,opmode(0, 0, 0, 0, iABC) /* OP_RETURN1 */
,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP1 */

View File

@ -1,5 +1,5 @@
/*
** $Id: lopcodes.h,v 1.184 2018/01/28 15:13:26 roberto Exp roberto $
** $Id: lopcodes.h,v 1.186 2018/02/07 15:18:04 roberto Exp roberto $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
@ -268,6 +268,7 @@ OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_RETVARARG,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_RETURN0,/* return */
OP_RETURN1,/* A return R(A) */
@ -286,7 +287,7 @@ OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx]) */
OP_VARARG,/* A B C R(A), R(A+1), ..., R(A+C-2) = vararg(B) */
OP_VARARG,/* A B C R(A), R(A+1), ..., R(A+C-2) = vararg */
OP_PREPVARARG,/*A (adjust vararg parameters) */
@ -305,9 +306,10 @@ OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
OP_SETLIST) may use 'top'.
(*) In OP_VARARG, if (C == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0). B is the vararg parameter.
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to 'top'.
(*) In OP_RETURN/OP_RETVARARG, if (B == 0) then return up to 'top'.
(OP_RETVARARG is the return instruction for vararg functions.)
(*) In OP_SETLIST, if (B == 0) then real B = 'top'; if (C == 0) then
next 'instruction' is EXTRAARG(real C).

View File

@ -1,5 +1,5 @@
/*
** $Id: lparser.c,v 2.175 2017/12/22 14:16:46 roberto Exp roberto $
** $Id: lparser.c,v 2.176 2018/02/07 15:18:04 roberto Exp roberto $
** Lua Parser
** See Copyright Notice in lua.h
*/
@ -568,6 +568,7 @@ static void close_func (LexState *ls) {
Proto *f = fs->f;
luaK_ret(fs, 0, 0); /* final return */
leaveblock(fs);
lua_assert(fs->bl == NULL);
luaK_finish(fs);
luaM_shrinkvector(L, f->code, f->sizecode, fs->pc, Instruction);
luaM_shrinkvector(L, f->lineinfo, f->sizelineinfo, fs->pc, ls_byte);
@ -577,7 +578,8 @@ static void close_func (LexState *ls) {
luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
lua_assert(fs->bl == NULL);
if (f->is_vararg)
f->maxstacksize++; /* ensure space to copy the function */
ls->fs = fs->prev;
luaC_checkGC(L);
}
@ -781,11 +783,6 @@ static void parlist (LexState *ls) {
}
case TK_DOTS: { /* param -> '...' */
luaX_next(ls);
if (testnext(ls, '='))
new_localvar(ls, str_checkname(ls));
else
new_localvarliteral(ls, "_ARG");
nparams++;
isvararg = 1;
break;
}
@ -795,10 +792,8 @@ static void parlist (LexState *ls) {
}
adjustlocalvars(ls, nparams);
f->numparams = cast_byte(fs->nactvar);
if (isvararg) {
f->numparams--; /* exclude vararg parameter */
if (isvararg)
setvararg(fs, f->numparams); /* declared vararg */
}
luaK_reserveregs(fs, fs->nactvar); /* reserve registers for parameters */
}
@ -984,10 +979,9 @@ static void simpleexp (LexState *ls, expdesc *v) {
}
case TK_DOTS: { /* vararg */
FuncState *fs = ls->fs;
int lastparam = fs->f->numparams;
check_condition(ls, fs->f->is_vararg,
"cannot use '...' outside a vararg function");
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, lastparam, 1));
init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 0, 1));
break;
}
case '{': { /* constructor */
@ -1703,10 +1697,6 @@ static void mainfunc (LexState *ls, FuncState *fs) {
expdesc v;
open_func(ls, fs, &bl);
setvararg(fs, 0); /* main function is always declared vararg */
fs->f->numparams = 0;
new_localvarliteral(ls, "_ARG");
adjustlocalvars(ls, 1);
luaK_reserveregs(fs, 1); /* reserve register for vararg */
init_exp(&v, VLOCAL, 0); /* create and... */
newupvalue(fs, ls->envn, &v); /* ...set environment upvalue */
luaX_next(ls); /* read first token */

View File

@ -1,5 +1,5 @@
/*
** $Id: lstate.h,v 2.152 2017/11/23 16:35:54 roberto Exp roberto $
** $Id: lstate.h,v 2.153 2017/12/19 16:40:17 roberto Exp roberto $
** Global State
** See Copyright Notice in lua.h
*/
@ -92,6 +92,7 @@ typedef struct CallInfo {
struct { /* only for Lua functions */
const Instruction *savedpc;
l_signalT trap;
int nextraargs; /* # of extra arguments in vararg functions */
} l;
struct { /* only for C functions */
lua_KFunction k; /* continuation in case of yields */

61
ltm.c
View File

@ -1,5 +1,5 @@
/*
** $Id: ltm.c,v 2.58 2018/01/28 13:39:52 roberto Exp roberto $
** $Id: ltm.c,v 2.59 2018/02/07 15:18:04 roberto Exp roberto $
** Tag methods
** See Copyright Notice in lua.h
*/
@ -216,41 +216,32 @@ int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
}
void luaT_adjustvarargs (lua_State *L, int nfixparams, StkId base) {
void luaT_adjustvarargs (lua_State *L, int nfixparams, CallInfo *ci) {
int i;
Table *vtab;
TValue nname;
int actual = cast_int(L->top - base); /* number of arguments */
int actual = cast_int(L->top - ci->func) - 1; /* number of arguments */
int nextra = actual - nfixparams; /* number of extra arguments */
vtab = luaH_new(L); /* create vararg table */
sethvalue2s(L, L->top, vtab); /* anchor it for resizing */
L->top++; /* space ensured by caller */
luaH_resize(L, vtab, nextra, 1);
for (i = 0; i < nextra; i++) /* put extra arguments into vararg table */
setobj2n(L, &vtab->array[i], s2v(L->top - nextra + i - 1));
setsvalue(L, &nname, G(L)->nfield); /* get field 'n' */
setivalue(luaH_set(L, vtab, &nname), nextra); /* store counter there */
L->top -= nextra; /* remove extra elements from the stack */
sethvalue2s(L, L->top - 1, vtab); /* move table to new top */
luaC_checkGC(L);
}
void luaT_getvarargs (lua_State *L, TValue *t, StkId where, int wanted) {
if (!ttistable(t))
luaG_runerror(L, "'vararg' parameter is not a table");
else {
int i;
Table *h = hvalue(t);
if (wanted < 0) { /* get all? */
const TValue *ns = luaH_getstr(h, G(L)->nfield);
int n = (ttisinteger(ns)) ? cast_int(ivalue(ns)) : 0;
wanted = n;
checkstackp(L, n, where);
L->top = where + n;
}
for (i = 0; i < wanted; i++) /* get what is available */
setobj2s(L, where + i, luaH_getint(h, i + 1));
return;
ci->u.l.nextraargs = nextra;
checkstackGC(L, nfixparams + 1);
/* copy function and fixed parameters to the top of the stack */
for (i = 0; i <= nfixparams; i++) {
setobjs2s(L, L->top++, ci->func + i);
setnilvalue(s2v(ci->func + i)); /* erase original copy (for GC) */
}
ci->func += actual + 1;
ci->top += actual + 1;
}
void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
int i;
int nextra = ci->u.l.nextraargs;
if (wanted < 0) {
wanted = nextra; /* get all extra arguments available */
checkstackp(L, nextra, where); /* ensure stack space */
L->top = where + nextra; /* next instruction will need top */
}
for (i = 0; i < wanted && i < nextra; i++)
setobjs2s(L, where + i, ci->func - nextra + i);
for (; i < wanted; i++) /* complete required results with nil */
setnilvalue(s2v(where + i));
}

10
ltm.h
View File

@ -1,5 +1,5 @@
/*
** $Id: ltm.h,v 2.29 2018/01/28 13:39:52 roberto Exp roberto $
** $Id: ltm.h,v 2.30 2018/02/07 15:18:04 roberto Exp roberto $
** Tag methods
** See Copyright Notice in lua.h
*/
@ -9,6 +9,7 @@
#include "lobject.h"
#include "lstate.h"
/*
@ -77,9 +78,10 @@ LUAI_FUNC int luaT_callorderTM (lua_State *L, const TValue *p1,
LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams, StkId base);
LUAI_FUNC void luaT_getvarargs (lua_State *L, TValue *t, StkId where,
int wanted);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
struct CallInfo *ci);
LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
StkId where, int wanted);
#endif

40
lvm.c
View File

@ -1,5 +1,5 @@
/*
** $Id: lvm.c,v 2.337 2018/02/06 19:16:56 roberto Exp roberto $
** $Id: lvm.c,v 2.338 2018/02/07 15:18:04 roberto Exp roberto $
** Lua virtual machine
** See Copyright Notice in lua.h
*/
@ -1506,14 +1506,10 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
luaF_close(L, base); /* close upvalues from current call */
if (!ttisLclosure(vra)) { /* C function? */
ProtectNT(luaD_call(L, ra, LUA_MULTRET)); /* call it */
if (trap) {
updatebase(ci);
ra = RA(i);
}
luaD_poscall(L, ci, ra, cast_int(L->top - ra));
return;
}
else { /* Lua tail call */
if (cl->p->is_vararg)
ci->func -= cl->p->numparams + ci->u.l.nextraargs + 1;
luaD_pretailcall(L, ci, ra, b); /* prepare call frame */
goto tailcall;
}
@ -1521,11 +1517,30 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_RETURN) {
int b = GETARG_B(i);
int n = (b != 0 ? b - 1 : cast_int(L->top - ra));
if (TESTARG_k(i))
luaF_close(L, base);
halfProtect(
luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra)))
);
halfProtect(luaD_poscall(L, ci, ra, n));
return;
}
vmcase(OP_RETVARARG) {
int b = GETARG_B(i);
int nparams = GETARG_C(i);
int nres = (b != 0 ? b - 1 : cast_int(L->top - ra));
int delta = ci->u.l.nextraargs + nparams + 2;
if (TESTARG_k(i))
luaF_close(L, base);
savepc(L);
/* code similar to 'luaD_poscall', but with a delta */
if (L->hookmask) {
luaD_rethook(L, ci);
if (ci->u.l.trap) {
updatebase(ci);
ra = RA(i);
}
}
L->ci = ci->previous; /* back to caller */
luaD_moveresults(L, ra, base - delta, nres, ci->nresults);
return;
}
vmcase(OP_RETURN0) {
@ -1702,12 +1717,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
}
vmcase(OP_VARARG) {
int n = GETARG_C(i) - 1; /* required results */
TValue *vtab = vRB(i); /* vararg table */
Protect(luaT_getvarargs(L, vtab, ra, n));
ProtectNT(luaT_getvarargs(L, ci, ra, n));
vmbreak;
}
vmcase(OP_PREPVARARG) {
luaT_adjustvarargs(L, GETARG_A(i), base);
luaT_adjustvarargs(L, GETARG_A(i), ci);
updatetrap(ci);
if (trap) {
luaD_hookcall(L, ci);