Replace upvalue-based shell dispatch with OP_LUSH opcode

Add a dedicated OP_LUSH A B instruction that loads shell C functions
directly from the registry, eliminating the need for upvalue allocation
in mainfunc(), upvalue population in lua_load(), and the fragile
LUSH_NUM_UPVALS heuristic. Every chunk no longer carries 4 extra
upvalues.
This commit is contained in:
Cormac Shannon
2026-03-12 22:47:25 +00:00
parent f88b17959f
commit f09a033160
9 changed files with 46 additions and 78 deletions

View File

@@ -501,15 +501,14 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
/*
** Look up a lush shell function (e.g. __command) that is registered
** as an upvalue on the main chunk. singlevaraux will find it directly
** as VUPVAL, emitting OP_GETUPVAL instead of OP_GETTABUP.
** Emit OP_LUSH to load a shell C function into a register.
** 'lushop' selects which function (LUSH_OP_COMMAND, etc.).
*/
static void buildlushvar (LexState *ls, TString *varname, expdesc *var) {
FuncState *fs = ls->fs;
init_exp(var, VGLOBAL, -1);
singlevaraux(fs, varname, var, 1);
lua_assert(var->k == VUPVAL); /* must be found as upvalue */
static void codelushfunc (FuncState *fs, int lushop, expdesc *var) {
int reg = fs->freereg;
luaK_reserveregs(fs, 1);
init_exp(var, VNONRELOC, reg);
luaK_codeABC(fs, OP_LUSH, reg, lushop, 0);
}
@@ -564,11 +563,8 @@ static void codecommand (LexState *ls, expdesc *v, expdesc *cmdstr) {
FuncState *fs = ls->fs;
int base, line;
expdesc func;
TString *cmdname = luaX_newstring(ls, "__command", 9);
line = ls->linenumber;
/* look up __command as upvalue */
buildlushvar(ls, cmdname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_COMMAND, &func);
base = func.u.info;
/* push the command string argument */
luaK_exp2nextreg(fs, cmdstr);
@@ -1254,11 +1250,9 @@ static void commandexp (LexState *ls, expdesc *v) {
expdesc func, cmdstr;
int base, nconcat = 0;
int line = ls->linenumber;
TString *cmdname = luaX_newstring(ls, "__command", 9);
TString *tsname = luaX_newstring(ls, "tostring", 8);
/* load __command function first so it occupies the base register */
buildlushvar(ls, cmdname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_COMMAND, &func);
base = func.u.info;
/* load the first fragment */
codestring(&cmdstr, ls->t.seminfo.ts);
@@ -1320,10 +1314,8 @@ static void codeenvget (LexState *ls, expdesc *v, TString *name) {
FuncState *fs = ls->fs;
int base, line;
expdesc func, arg;
TString *fname = luaX_newstring(ls, "__getenv", 8);
line = ls->linenumber;
buildlushvar(ls, fname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_GETENV, &func);
base = func.u.info;
codestring(&arg, name);
luaK_exp2nextreg(fs, &arg);
@@ -2202,13 +2194,10 @@ static void envstat (LexState *ls) {
TString *name = ls->t.seminfo.ts;
int base, line;
expdesc func, arg, val;
TString *fname;
line = ls->linenumber;
luaX_next(ls); /* skip TK_ENVVAR */
checknext(ls, '=');
fname = luaX_newstring(ls, "__setenv", 8);
buildlushvar(ls, fname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_SETENV, &func);
base = func.u.info;
codestring(&arg, name);
luaK_exp2nextreg(fs, &arg);
@@ -2231,10 +2220,8 @@ static void interactivestat (LexState *ls) {
/* simple interactive command, no interpolation */
int base, line;
expdesc func, cmdstr, v;
TString *fname = luaX_newstring(ls, "__interactive", 13);
line = ls->linenumber;
buildlushvar(ls, fname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_INTERACTIVE, &func);
base = func.u.info;
codestring(&cmdstr, ls->t.seminfo.ts);
luaK_exp2nextreg(fs, &cmdstr);
@@ -2248,10 +2235,8 @@ static void interactivestat (LexState *ls) {
expdesc func, cmdstr, v;
int base, nconcat = 0;
int line = ls->linenumber;
TString *fname = luaX_newstring(ls, "__interactive", 13);
TString *tsname = luaX_newstring(ls, "tostring", 8);
buildlushvar(ls, fname, &func);
luaK_exp2nextreg(fs, &func);
codelushfunc(fs, LUSH_OP_INTERACTIVE, &func);
base = func.u.info;
/* load the first fragment */
codestring(&cmdstr, ls->t.seminfo.ts);
@@ -2410,12 +2395,8 @@ static void statement (LexState *ls) {
** upvalue named LUA_ENV
*/
static void mainfunc (LexState *ls, FuncState *fs) {
static const char *const lush_upval_names[] = {
"__command", "__interactive", "__getenv", "__setenv"
};
BlockCnt bl;
Upvaldesc *env;
int i;
open_func(ls, fs, &bl);
setvararg(fs, PF_ISVARARG); /* main function is always vararg */
env = allocupvalue(fs); /* ...set environment upvalue */
@@ -2424,15 +2405,6 @@ static void mainfunc (LexState *ls, FuncState *fs) {
env->kind = VDKREG;
env->name = ls->envn;
luaC_objbarrier(ls->L, fs->f, env->name);
/* allocate upvalues for lush shell functions */
for (i = 0; i < 4; i++) {
Upvaldesc *uv = allocupvalue(fs);
uv->instack = 1;
uv->idx = 0;
uv->kind = VDKREG;
uv->name = luaS_new(ls->L, lush_upval_names[i]);
luaC_objbarrier(ls->L, fs->f, uv->name);
}
luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */
check(ls, TK_EOS);
@@ -2444,7 +2416,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
Dyndata *dyd, const char *name, int firstchar) {
LexState lexstate;
FuncState funcstate;
LClosure *cl = luaF_newLclosure(L, LUSH_NUM_UPVALS); /* create main closure */
LClosure *cl = luaF_newLclosure(L, 1); /* create main closure */
setclLvalue2s(L, L->top.p, cl); /* anchor it (to avoid being collected) */
luaD_inctop(L);
lexstate.h = luaH_new(L); /* create table for scanner */
@@ -2459,7 +2431,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
dyd->actvar.n = dyd->gt.n = dyd->label.n = 0;
luaX_setinput(L, &lexstate, z, funcstate.f->source, firstchar);
mainfunc(&lexstate, &funcstate);
lua_assert(!funcstate.prev && funcstate.nups == LUSH_NUM_UPVALS && !lexstate.fs);
lua_assert(!funcstate.prev && funcstate.nups == 1 && !lexstate.fs);
/* all scopes should be correctly finished */
lua_assert(dyd->actvar.n == 0 && dyd->gt.n == 0 && dyd->label.n == 0);
L->top.p--; /* remove scanner's table */