Implement lightweight functions.

When a function does not use eval, arguments, with and has no inner
functions it does not need a closure. We can call it without creating
a variable object if we store local variables on the stack instead.
This commit is contained in:
Tor Andersson
2014-02-10 18:00:13 +01:00
parent e39e0bdcd1
commit 68862386d8
6 changed files with 184 additions and 56 deletions

View File

@@ -102,6 +102,24 @@ static int addstring(JF, const char *value)
return F->strlen++;
}
static void addlocal(JF, const char *name)
{
if (F->varlen >= F->varcap) {
F->varcap = F->varcap ? F->varcap * 2 : 16;
F->vartab = realloc(F->vartab, F->varcap * sizeof *F->vartab);
}
F->vartab[F->varlen++] = name;
}
static int findlocal(JF, const char *name)
{
int i;
for (i = 0; i < F->varlen; ++i)
if (!strcmp(F->vartab[i], name))
return i;
return -1;
}
static void emitfunction(JF, js_Function *fun)
{
emit(J, F, OP_CLOSURE);
@@ -129,6 +147,20 @@ static void emitstring(JF, int opcode, const char *str)
emitraw(J, F, addstring(J, F, str));
}
static void emitlocal(JF, int oploc, int opvar, const char *name)
{
int i;
if (F->lightweight) {
i = findlocal(J, F, name);
if (i >= 0) {
emit(J, F, oploc);
emitraw(J, F, i + 1);
return;
}
}
emitstring(J, F, opvar, name);
}
static int here(JF)
{
return F->codelen;
@@ -241,7 +273,7 @@ static void cassign(JF, js_Ast *lhs, js_Ast *rhs)
switch (lhs->type) {
case EXP_IDENTIFIER:
cexp(J, F, rhs);
emitstring(J, F, OP_SETVAR, lhs->string);
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
break;
case EXP_INDEX:
cexp(J, F, lhs->a);
@@ -267,14 +299,14 @@ static void cassignforin(JF, js_Ast *stm)
if (stm->type == STM_FOR_IN_VAR) {
if (lhs->b)
jsC_error(J, lhs->b, "more than one loop variable in for-in statement");
emitstring(J, F, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->a->a->string); /* list(var-init(ident)) */
emit(J, F, OP_POP);
return;
}
switch (lhs->type) {
case EXP_IDENTIFIER:
emitstring(J, F, OP_SETVAR, lhs->string);
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
emit(J, F, OP_POP);
break;
case EXP_INDEX:
@@ -300,7 +332,7 @@ static void cassignop1(JF, js_Ast *lhs, int dup)
{
switch (lhs->type) {
case EXP_IDENTIFIER:
emitstring(J, F, OP_GETVAR, lhs->string);
emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, lhs->string);
if (dup) emit(J, F, OP_DUP);
break;
case EXP_INDEX:
@@ -326,7 +358,7 @@ static void cassignop2(JF, js_Ast *lhs)
{
switch (lhs->type) {
case EXP_IDENTIFIER:
emitstring(J, F, OP_SETVAR, lhs->string);
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, lhs->string);
break;
case EXP_INDEX:
break;
@@ -351,7 +383,7 @@ static void cdelete(JF, js_Ast *exp)
{
switch (exp->type) {
case EXP_IDENTIFIER:
emitstring(J, F, OP_DELVAR, exp->string);
emitlocal(J, F, OP_DELLOCAL, OP_DELVAR, exp->string);
break;
case EXP_INDEX:
cexp(J, F, exp->a);
@@ -430,7 +462,7 @@ static void cexp(JF, js_Ast *exp)
break;
case EXP_IDENTIFIER:
emitstring(J, F, OP_GETVAR, exp->string);
emitlocal(J, F, OP_GETLOCAL, OP_GETVAR, exp->string);
break;
case EXP_INDEX:
@@ -818,7 +850,7 @@ static void cvarinit(JF, js_Ast *list)
js_Ast *var = list->a;
if (var->b) {
cexp(J, F, var->b);
emitstring(J, F, OP_SETVAR, var->a->string);
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, var->a->string);
emit(J, F, OP_POP);
}
list = list->b;
@@ -1036,6 +1068,40 @@ static void cstmlist(JF, js_Ast *list)
}
}
/* Analyze */
static void analyze(JF, js_Ast *node)
{
if (isfun(node->type)) {
F->lightweight = 0;
return; /* don't scan inner functions */
}
if (node->type == STM_WITH) {
F->lightweight = 0;
}
if (node->type == EXP_IDENTIFIER) {
if (!strcmp(node->string, "arguments")) {
F->lightweight = 0;
F->arguments = 1;
} else if (!strcmp(node->string, "eval")) {
/* eval may only be used as a direct function call */
if (!node->parent || node->parent->type != EXP_CALL || node->parent->a != node)
js_evalerror(J, "%s:%d: illegal use of 'eval'", J->filename, node->line);
F->lightweight = 0;
}
}
if (node->type == EXP_VAR)
addlocal(J, F, node->a->string);
if (node->a) analyze(J, F, node->a);
if (node->b) analyze(J, F, node->b);
if (node->c) analyze(J, F, node->c);
if (node->d) analyze(J, F, node->d);
}
/* Declarations and programs */
static int listlength(js_Ast *list)
@@ -1048,62 +1114,71 @@ static int listlength(js_Ast *list)
static void cparams(JF, js_Ast *list)
{
F->numparams = listlength(list);
F->params = malloc(F->numparams * sizeof *F->params);
int i = 0;
while (list) {
F->params[i++] = list->a->string;
addlocal(J, F, list->a->string);
list = list->b;
}
}
static void cvardecs(JF)
{
int i;
for (i = 0; i < F->varlen; ++i) {
emit(J, F, OP_UNDEF);
emitstring(J, F, OP_INITVAR, F->vartab[i]);
}
}
static void cfundecs(JF, js_Ast *list)
{
while (list) {
js_Ast *stm = list->a;
if (stm->type == AST_FUNDEC) {
addlocal(J, F, stm->a->string);
emitfunction(J, F, newfun(J, stm->a, stm->b, stm->c, 0));
emitstring(J, F, OP_FUNDEC, stm->a->string);
emitstring(J, F, OP_INITVAR, stm->a->string);
}
list = list->b;
}
}
static void cvardecs(JF, js_Ast *node)
{
if (node->type == EXP_VAR) {
emitstring(J, F, OP_VARDEC, node->a->string);
} else if (!isfun(node->type)) {
if (node->a) cvardecs(J, F, node->a);
if (node->b) cvardecs(J, F, node->b);
if (node->c) cvardecs(J, F, node->c);
if (node->d) cvardecs(J, F, node->d);
}
}
static void cfunbody(JF, js_Ast *name, js_Ast *params, js_Ast *body)
{
F->lightweight = 1;
F->arguments = 0;
cparams(J, F, params);
if (name) {
F->name = name->string;
emitfunction(J, F, F);
emitstring(J, F, OP_FUNDEC, name->string);
addlocal(J, F, name->string);
} else {
F->name = "";
}
cparams(J, F, params);
if (body)
analyze(J, F, body);
if (F->script)
emit(J, F, OP_UNDEF);
if (body) {
if (!F->lightweight) {
cvardecs(J, F);
cfundecs(J, F, body);
cvardecs(J, F, body);
cstmlist(J, F, body);
}
if (name) {
if (F->lightweight)
emit(J, F, OP_CURRENT);
else
emitfunction(J, F, F);
emitlocal(J, F, OP_SETLOCAL, OP_SETVAR, name->string);
emit(J, F, OP_POP);
}
if (F->script) {
emit(J, F, OP_UNDEF);
cstmlist(J, F, body);
emit(J, F, OP_RETURN);
} else {
cstmlist(J, F, body);
emit(J, F, OP_UNDEF);
emit(J, F, OP_RETURN);
}

View File

@@ -34,10 +34,14 @@ enum js_OpCode
OP_FALSE,
OP_THIS,
OP_GLOBAL,
OP_CURRENT, /* currently executing function object */
OP_FUNDEC, /* <closure> -S- */
OP_VARDEC, /* -S- */
OP_INITLOCAL, /* <value> -N- */
OP_GETLOCAL, /* -N- <value> */
OP_SETLOCAL, /* <value> -N- <value> */
OP_DELLOCAL, /* -N- false */
OP_INITVAR, /* <value> -S- */
OP_GETVAR, /* -S- <value> */
OP_SETVAR, /* <value> -S- <value> */
OP_DELVAR, /* -S- <success> */
@@ -115,9 +119,9 @@ struct js_Function
{
const char *name;
int script;
int lightweight;
int arguments;
int numparams;
const char **params;
short *code;
int codecap, codelen;
@@ -131,6 +135,9 @@ struct js_Function
const char **strtab;
int strcap, strlen;
const char **vartab;
int varcap, varlen;
const char *filename;
int line;

View File

@@ -716,11 +716,13 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
short *end = F->code + F->codelen;
int i;
printf("function %p %s (", F, F->name);
for (i = 0; i < F->numparams; ++i)
printf("%s%s", i > 0 ? ", " : "", F->params[i]);
printf(")\n");
printf("function %p %s\n", F, F->name);
printf("\tsource %s:%d\n", F->filename, F->line);
printf("\tlightweight:%d\n", F->lightweight);
printf("\tparameters:%d\n", F->numparams);
printf("\targuments:%d\n", F->arguments);
for (i = 0; i < F->varlen; ++i)
printf("\tlocal %d %s\n", i + 1, F->vartab[i]);
for (i = 0; i < F->funlen; ++i)
printf("\tfunction %p %s\n", F->funtab[i], F->funtab[i]->name);
for (i = 0; i < F->strlen; ++i) {
@@ -753,8 +755,7 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
p += 2;
break;
case OP_FUNDEC:
case OP_VARDEC:
case OP_INITVAR:
case OP_GETVAR:
case OP_SETVAR:
case OP_DELVAR:
@@ -767,6 +768,10 @@ void jsC_dumpfunction(js_State *J, js_Function *F)
ps(F->strtab[*p++]);
break;
case OP_INITLOCAL:
case OP_GETLOCAL:
case OP_SETLOCAL:
case OP_DELLOCAL:
case OP_NUMBER_N:
case OP_INITPROP_N:
case OP_CALL:

View File

@@ -63,14 +63,14 @@ static int Fp_toString(js_State *J, int argc)
n = strlen("function () { ... }");
n += strlen(F->name);
for (i = 0; i < F->numparams; ++i)
n += strlen(F->params[i]) + 1;
n += strlen(F->vartab[i]) + 1;
s = malloc(n);
strcpy(s, "function ");
strcat(s, F->name);
strcat(s, "(");
for (i = 0; i < F->numparams; ++i) {
if (i > 0) strcat(s, ",");
strcat(s, F->params[i]);
strcat(s, F->vartab[i]);
}
strcat(s, ") { ... }");
js_pushstring(J, s);

2
jsgc.c
View File

@@ -14,10 +14,10 @@ static void jsG_freeenvironment(js_State *J, js_Environment *env)
static void jsG_freefunction(js_State *J, js_Function *fun)
{
free(fun->params);
free(fun->funtab);
free(fun->numtab);
free(fun->strtab);
free(fun->vartab);
free(fun->code);
free(fun);
}

61
jsrun.c
View File

@@ -753,6 +753,31 @@ static int js_delvar(js_State *J, const char *name)
/* Function calls */
static void jsR_calllwfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
{
js_Environment *saveE;
js_Value v;
int i;
saveE = J->E;
J->E = scope;
if (n > F->numparams) {
js_pop(J, F->numparams - n);
n = F->numparams;
}
for (i = n; i < F->varlen; ++i)
js_pushundefined(J);
jsR_run(J, F);
v = js_tovalue(J, -1);
TOP = --BOT; /* clear stack */
js_pushvalue(J, v);
J->E = saveE;
}
static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment *scope)
{
js_Environment *saveE;
@@ -764,10 +789,10 @@ static void jsR_callfunction(js_State *J, int n, js_Function *F, js_Environment
J->E = jsR_newenvironment(J, jsV_newobject(J, JS_COBJECT, NULL), scope);
for (i = 0; i < F->numparams; ++i) {
if (i < n)
js_decvar(J, F->params[i], i + 1);
js_decvar(J, F->vartab[i], i + 1);
else {
js_pushundefined(J);
js_decvar(J, F->params[i], -1);
js_decvar(J, F->vartab[i], -1);
js_pop(J, 1);
}
}
@@ -822,9 +847,12 @@ void js_call(js_State *J, int n)
savebot = BOT;
BOT = TOP - n - 1;
if (obj->type == JS_CFUNCTION)
jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
else if (obj->type == JS_CSCRIPT)
if (obj->type == JS_CFUNCTION) {
if (obj->u.f.function->lightweight)
jsR_calllwfunction(J, n, obj->u.f.function, obj->u.f.scope);
else
jsR_callfunction(J, n, obj->u.f.function, obj->u.f.scope);
} else if (obj->type == JS_CSCRIPT)
jsR_callscript(J, n, obj->u.f.function);
else if (obj->type == JS_CCFUNCTION)
jsR_callcfunction(J, n, obj->u.c.length, obj->u.c.function);
@@ -992,14 +1020,27 @@ static void jsR_run(js_State *J, js_Function *F)
case OP_THIS: js_copy(J, 0); break;
case OP_GLOBAL: js_pushobject(J, J->G); break;
case OP_CURRENT: js_currentfunction(J); break;
case OP_FUNDEC:
js_decvar(J, ST[*pc++], -1);
js_pop(J, 1);
case OP_INITLOCAL:
STACK[BOT + *pc++] = STACK[--TOP];
break;
case OP_VARDEC:
js_pushundefined(J);
case OP_GETLOCAL:
CHECKSTACK(1);
STACK[TOP++] = STACK[BOT + *pc++];
break;
case OP_SETLOCAL:
STACK[BOT + *pc++] = STACK[TOP-1];
break;
case OP_DELLOCAL:
++pc;
js_pushboolean(J, 0);
break;
case OP_INITVAR:
js_decvar(J, ST[*pc++], -1);
js_pop(J, 1);
break;