First implementation of constant propagation

Local constant variables initialized with compile-time constants
are optimized away from the code.
This commit is contained in:
Roberto Ierusalimschy 2019-07-12 11:38:42 -03:00
parent be8445d7e4
commit f6aab3ec1f
12 changed files with 249 additions and 119 deletions

56
lcode.c
View File

@ -67,6 +67,30 @@ static int tonumeral (const expdesc *e, TValue *v) {
} }
/*
** If expression is a constant, fills 'v' with its value
** and returns 1. Otherwise, returns 0.
*/
int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
if (hasjumps(e))
return 0; /* not a constant */
switch (e->k) {
case VFALSE: case VTRUE:
setbvalue(v, e->k == VTRUE);
return 1;
case VNIL:
setnilvalue(v);
return 1;
case VK: {
TValue *k = &fs->f->k[e->u.info];
setobj(fs->ls->L, v, k);
return 1;
}
default: return tonumeral(e, v);
}
}
/* /*
** Return the previous instruction of the current code. If there ** Return the previous instruction of the current code. If there
** may be a jump target between the current instruction and the ** may be a jump target between the current instruction and the
@ -629,6 +653,31 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
} }
/*
** Convert a constant in 'v' into an expression description 'e'
*/
static void const2exp (FuncState *fs, TValue *v, expdesc *e) {
switch (ttypetag(v)) {
case LUA_TNUMINT:
e->k = VKINT; e->u.ival = ivalue(v);
break;
case LUA_TNUMFLT:
e->k = VKFLT; e->u.nval = fltvalue(v);
break;
case LUA_TBOOLEAN:
e->k = bvalue(v) ? VTRUE : VFALSE;
break;
case LUA_TNIL:
e->k = VNIL;
break;
case LUA_TSHRSTR: case LUA_TLNGSTR:
e->k = VK; e->u.info = luaK_stringK(fs, tsvalue(v));
break;
default: lua_assert(0);
}
}
/* /*
** Fix an expression to return the number of results 'nresults'. ** Fix an expression to return the number of results 'nresults'.
** Either 'e' is a multi-ret expression (function call or vararg) ** Either 'e' is a multi-ret expression (function call or vararg)
@ -677,6 +726,11 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
*/ */
void luaK_dischargevars (FuncState *fs, expdesc *e) { void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) { switch (e->k) {
case VCONST: {
TValue *val = &fs->ls->dyd->actvar.arr[e->u.info].k;
const2exp(fs, val, e);
break;
}
case VLOCAL: { /* already in a register */ case VLOCAL: { /* already in a register */
e->u.info = e->u.var.sidx; e->u.info = e->u.var.sidx;
e->k = VNONRELOC; /* becomes a non-relocatable value */ e->k = VNONRELOC; /* becomes a non-relocatable value */
@ -1074,7 +1128,6 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) {
** Code 'not e', doing constant folding. ** Code 'not e', doing constant folding.
*/ */
static void codenot (FuncState *fs, expdesc *e) { static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) { switch (e->k) {
case VNIL: case VFALSE: { case VNIL: case VFALSE: {
e->k = VTRUE; /* true == not nil == not false */ e->k = VTRUE; /* true == not nil == not false */
@ -1447,6 +1500,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
*/ */
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
luaK_dischargevars(fs, e);
switch (op) { switch (op) {
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
if (constfolding(fs, op + LUA_OPUNM, e, &ef)) if (constfolding(fs, op + LUA_OPUNM, e, &ef))

View File

@ -56,6 +56,7 @@ LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
int B, int C, int k); int B, int C, int k);
LUAI_FUNC int luaK_isKint (expdesc *e); LUAI_FUNC int luaK_isKint (expdesc *e);
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line); LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);

View File

@ -149,7 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
DumpByte(f->upvalues[i].instack, D); DumpByte(f->upvalues[i].instack, D);
DumpByte(f->upvalues[i].idx, D); DumpByte(f->upvalues[i].idx, D);
DumpByte(f->upvalues[i].ro, D); DumpByte(f->upvalues[i].kind, D);
} }
} }

View File

@ -460,7 +460,7 @@ typedef struct Upvaldesc {
TString *name; /* upvalue name (for debug information) */ TString *name; /* upvalue name (for debug information) */
lu_byte instack; /* whether it is in stack (register) */ lu_byte instack; /* whether it is in stack (register) */
lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */
lu_byte ro; /* true if upvalue is read-only (const) */ lu_byte kind; /* kind of corresponding variable */
} Upvaldesc; } Upvaldesc;

105
lparser.c
View File

@ -170,15 +170,16 @@ static void codename (LexState *ls, expdesc *e) {
** Register a new local variable in the active 'Proto' (for debug ** Register a new local variable in the active 'Proto' (for debug
** information). ** information).
*/ */
static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) { static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
Proto *f = fs->f; Proto *f = fs->f;
int oldsize = f->sizelocvars; int oldsize = f->sizelocvars;
luaM_growvector(L, f->locvars, fs->ndebugvars, f->sizelocvars, luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
LocVar, SHRT_MAX, "local variables"); LocVar, SHRT_MAX, "local variables");
while (oldsize < f->sizelocvars) while (oldsize < f->sizelocvars)
f->locvars[oldsize++].varname = NULL; f->locvars[oldsize++].varname = NULL;
f->locvars[fs->ndebugvars].varname = varname; f->locvars[fs->ndebugvars].varname = varname;
luaC_objbarrier(L, f, varname); f->locvars[fs->ndebugvars].startpc = fs->pc;
luaC_objbarrier(ls->L, f, varname);
return fs->ndebugvars++; return fs->ndebugvars++;
} }
@ -191,16 +192,13 @@ static Vardesc *new_localvar (LexState *ls, TString *name) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
Dyndata *dyd = ls->dyd; Dyndata *dyd = ls->dyd;
Vardesc *var; Vardesc *var;
int reg = registerlocalvar(L, fs, name);
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
MAXVARS, "local variables"); MAXVARS, "local variables");
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
var = &dyd->actvar.arr[dyd->actvar.n++]; var = &dyd->actvar.arr[dyd->actvar.n++];
var->pidx = cast(short, reg); var->vd.kind = VDKREG; /* default is a regular variable */
var->ro = 0; var->vd.name = name;
var->name = name;
setnilvalue(var);
return var; return var;
} }
@ -225,8 +223,8 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
static int stacklevel (FuncState *fs, int nvar) { static int stacklevel (FuncState *fs, int nvar) {
while (nvar > 0) { while (nvar > 0) {
Vardesc *vd = getlocalvardesc(fs, nvar - 1); Vardesc *vd = getlocalvardesc(fs, nvar - 1);
if (vdinstack(vd)) /* is in the stack? */ if (vd->vd.kind != RDKCTC) /* is in the stack? */
return vd->sidx + 1; return vd->vd.sidx + 1;
else else
nvar--; /* try previous variable */ nvar--; /* try previous variable */
} }
@ -247,10 +245,10 @@ int luaY_nvarstack (FuncState *fs) {
*/ */
static LocVar *localdebuginfo (FuncState *fs, int i) { static LocVar *localdebuginfo (FuncState *fs, int i) {
Vardesc *vd = getlocalvardesc(fs, i); Vardesc *vd = getlocalvardesc(fs, i);
if (!vdinstack(vd)) if (vd->vd.kind == RDKCTC)
return NULL; /* no debug info. for constants */ return NULL; /* no debug info. for constants */
else { else {
int idx = vd->pidx; int idx = vd->vd.pidx;
lua_assert(idx < fs->ndebugvars); lua_assert(idx < fs->ndebugvars);
return &fs->f->locvars[idx]; return &fs->f->locvars[idx];
} }
@ -261,7 +259,7 @@ static void init_var (FuncState *fs, expdesc *e, int i) {
e->f = e->t = NO_JUMP; e->f = e->t = NO_JUMP;
e->k = VLOCAL; e->k = VLOCAL;
e->u.var.vidx = i; e->u.var.vidx = i;
e->u.var.sidx = getlocalvardesc(fs, i)->sidx; e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
} }
@ -269,15 +267,19 @@ static void check_readonly (LexState *ls, expdesc *e) {
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
TString *varname = NULL; /* to be set if variable is const */ TString *varname = NULL; /* to be set if variable is const */
switch (e->k) { switch (e->k) {
case VCONST: {
varname = ls->dyd->actvar.arr[e->u.info].vd.name;
break;
}
case VLOCAL: { case VLOCAL: {
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
if (vardesc->ro) if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
varname = vardesc->name; varname = vardesc->vd.name;
break; break;
} }
case VUPVAL: { case VUPVAL: {
Upvaldesc *up = &fs->f->upvalues[e->u.info]; Upvaldesc *up = &fs->f->upvalues[e->u.info];
if (up->ro) if (up->kind != VDKREG)
varname = up->name; varname = up->name;
break; break;
} }
@ -302,8 +304,8 @@ static void adjustlocalvars (LexState *ls, int nvars) {
for (i = 0; i < nvars; i++) { for (i = 0; i < nvars; i++) {
int varidx = fs->nactvar++; int varidx = fs->nactvar++;
Vardesc *var = getlocalvardesc(fs, varidx); Vardesc *var = getlocalvardesc(fs, varidx);
var->sidx = stklevel++; var->vd.sidx = stklevel++;
fs->f->locvars[var->pidx].startpc = fs->pc; var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
} }
} }
@ -354,13 +356,13 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
if (v->k == VLOCAL) { if (v->k == VLOCAL) {
up->instack = 1; up->instack = 1;
up->idx = v->u.var.sidx; up->idx = v->u.var.sidx;
up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro; up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name)); lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
} }
else { else {
up->instack = 0; up->instack = 0;
up->idx = cast_byte(v->u.info); up->idx = cast_byte(v->u.info);
up->ro = prev->f->upvalues[v->u.info].ro; up->kind = prev->f->upvalues[v->u.info].kind;
lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
} }
up->name = name; up->name = name;
@ -373,11 +375,17 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
** Look for an active local variable with the name 'n' in the ** Look for an active local variable with the name 'n' in the
** function 'fs'. ** function 'fs'.
*/ */
static int searchvar (FuncState *fs, TString *n) { static int searchvar (FuncState *fs, TString *n, expdesc *var) {
int i; int i;
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
if (eqstr(n, getlocalvardesc(fs, i)->name)) Vardesc *vd = getlocalvardesc(fs, i);
return i; if (eqstr(n, vd->vd.name)) { /* found? */
if (vd->vd.kind == RDKCTC) /* compile-time constant? */
init_exp(var, VCONST, fs->firstlocal + i);
else /* real variable */
init_var(fs, var, i);
return var->k;
}
} }
return -1; /* not found */ return -1; /* not found */
} }
@ -405,20 +413,19 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
if (fs == NULL) /* no more levels? */ if (fs == NULL) /* no more levels? */
init_exp(var, VVOID, 0); /* default is global */ init_exp(var, VVOID, 0); /* default is global */
else { else {
int v = searchvar(fs, n); /* look up locals at current level */ int v = searchvar(fs, n, var); /* look up locals at current level */
if (v >= 0) { /* found? */ if (v >= 0) { /* found? */
init_var(fs, var, v); /* variable is local */ if (v == VLOCAL && !base)
if (!base)
markupval(fs, var->u.var.vidx); /* local will be used as an upval */ markupval(fs, var->u.var.vidx); /* local will be used as an upval */
} }
else { /* not found as local at current level; try upvalues */ else { /* not found as local at current level; try upvalues */
int idx = searchupvalue(fs, n); /* try existing upvalues */ int idx = searchupvalue(fs, n); /* try existing upvalues */
if (idx < 0) { /* not found? */ if (idx < 0) { /* not found? */
singlevaraux(fs->prev, n, var, 0); /* try upper levels */ singlevaraux(fs->prev, n, var, 0); /* try upper levels */
if (var->k == VVOID) /* not found? */ if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */
return; /* it is a global */
/* else was LOCAL or UPVAL */
idx = newupvalue(fs, n, var); /* will be a new upvalue */ idx = newupvalue(fs, n, var); /* will be a new upvalue */
else /* it is a global or a constant */
return; /* don't need to do anything at this level */
} }
init_exp(var, VUPVAL, idx); /* new or old upvalue */ init_exp(var, VUPVAL, idx); /* new or old upvalue */
} }
@ -483,7 +490,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
** local variable. ** local variable.
*/ */
static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->name); const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
luaK_semerror(ls, msg); /* raise the error */ luaK_semerror(ls, msg); /* raise the error */
@ -1710,21 +1717,20 @@ static int getlocalattribute (LexState *ls) {
const char *attr = getstr(str_checkname(ls)); const char *attr = getstr(str_checkname(ls));
checknext(ls, '>'); checknext(ls, '>');
if (strcmp(attr, "const") == 0) if (strcmp(attr, "const") == 0)
return 1; /* read-only variable */ return RDKCONST; /* read-only variable */
else if (strcmp(attr, "toclose") == 0) else if (strcmp(attr, "toclose") == 0)
return 2; /* to-be-closed variable */ return RDKTOCLOSE; /* to-be-closed variable */
else else
luaK_semerror(ls, luaK_semerror(ls,
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
} }
return 0; return VDKREG;
} }
static void checktoclose (LexState *ls, int toclose) { static void checktoclose (LexState *ls, int level) {
if (toclose != -1) { /* is there a to-be-closed variable? */ if (level != -1) { /* is there a to-be-closed variable? */
FuncState *fs = ls->fs; FuncState *fs = ls->fs;
int level = luaY_nvarstack(fs) + toclose;
markupval(fs, level + 1); markupval(fs, level + 1);
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
luaK_codeABC(fs, OP_TBC, level, 0, 0); luaK_codeABC(fs, OP_TBC, level, 0, 0);
@ -1734,20 +1740,20 @@ static void checktoclose (LexState *ls, int toclose) {
static void localstat (LexState *ls) { static void localstat (LexState *ls) {
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
FuncState *fs = ls->fs;
int toclose = -1; /* index of to-be-closed variable (if any) */ int toclose = -1; /* index of to-be-closed variable (if any) */
Vardesc *var; /* last variable */
int nvars = 0; int nvars = 0;
int nexps; int nexps;
expdesc e; expdesc e;
do { do {
int kind = getlocalattribute(ls); int kind = getlocalattribute(ls);
Vardesc *var = new_localvar(ls, str_checkname(ls)); var = new_localvar(ls, str_checkname(ls));
if (kind != 0) { /* is there an attribute? */ var->vd.kind = kind;
var->ro = 1; /* all attributes make variable read-only */ if (kind == RDKTOCLOSE) { /* to-be-closed? */
if (kind == 2) { /* to-be-closed? */
if (toclose != -1) /* one already present? */ if (toclose != -1) /* one already present? */
luaK_semerror(ls, "multiple to-be-closed variables in local list"); luaK_semerror(ls, "multiple to-be-closed variables in local list");
toclose = nvars; toclose = luaY_nvarstack(fs) + nvars;
}
} }
nvars++; nvars++;
} while (testnext(ls, ',')); } while (testnext(ls, ','));
@ -1757,9 +1763,18 @@ static void localstat (LexState *ls) {
e.k = VVOID; e.k = VVOID;
nexps = 0; nexps = 0;
} }
if (nvars == nexps && /* no adjustments? */
var->vd.kind == RDKCONST && /* last variable is const? */
luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */
var->vd.kind = RDKCTC; /* variable is a compile-time constant */
adjustlocalvars(ls, nvars - 1); /* exclude last variable */
fs->nactvar++; /* but count it */
}
else {
adjust_assign(ls, nvars, nexps, &e); adjust_assign(ls, nvars, nexps, &e);
checktoclose(ls, toclose);
adjustlocalvars(ls, nvars); adjustlocalvars(ls, nvars);
}
checktoclose(ls, toclose);
} }
@ -1925,7 +1940,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
env = allocupvalue(fs); /* ...set environment upvalue */ env = allocupvalue(fs); /* ...set environment upvalue */
env->instack = 1; env->instack = 1;
env->idx = 0; env->idx = 0;
env->ro = 0; env->kind = VDKREG;
env->name = ls->envn; env->name = ls->envn;
luaX_next(ls); /* read first token */ luaX_next(ls); /* read first token */
statlist(ls); /* parse main body */ statlist(ls); /* parse main body */

View File

@ -34,8 +34,9 @@ typedef enum {
VNONRELOC, /* expression has its value in a fixed register; VNONRELOC, /* expression has its value in a fixed register;
info = result register */ info = result register */
VLOCAL, /* local variable; var.ridx = local register; VLOCAL, /* local variable; var.ridx = local register;
var.vidx = index in 'actvar.arr' */ var.vidx = relative index in 'actvar.arr' */
VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */
VINDEXED, /* indexed variable; VINDEXED, /* indexed variable;
ind.t = table register; ind.t = table register;
ind.idx = key's R index */ ind.idx = key's R index */
@ -81,19 +82,25 @@ typedef struct expdesc {
} expdesc; } expdesc;
/* kinds of variables */
#define VDKREG 0 /* regular */
#define RDKCONST 1 /* constant */
#define RDKTOCLOSE 2 /* to-be-closed */
#define RDKCTC 3 /* compile-time constant */
/* description of an active local variable */ /* description of an active local variable */
typedef struct Vardesc { typedef union Vardesc {
struct {
TValuefields; /* constant value (if it is a compile-time constant) */ TValuefields; /* constant value (if it is a compile-time constant) */
lu_byte ro; /* true if variable is 'const' */ lu_byte kind;
lu_byte sidx; /* index of the variable in the stack */ lu_byte sidx; /* index of the variable in the stack */
short pidx; /* index of the variable in the Proto's 'locvars' array */ short pidx; /* index of the variable in the Proto's 'locvars' array */
TString *name; /* variable name */ TString *name; /* variable name */
} vd;
TValue k; /* constant value (if any) */
} Vardesc; } Vardesc;
/* check whether Vardesc is in the stack (not a compile-time constant) */
#define vdinstack(vd) (ttisnil(vd))
/* description of pending goto statements and label statements */ /* description of pending goto statements and label statements */
typedef struct Labeldesc { typedef struct Labeldesc {

View File

@ -198,12 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
n = LoadInt(S); n = LoadInt(S);
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
f->sizeupvalues = n; f->sizeupvalues = n;
for (i = 0; i < n; i++)
f->upvalues[i].name = NULL;
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
f->upvalues[i].name = NULL;
f->upvalues[i].instack = LoadByte(S); f->upvalues[i].instack = LoadByte(S);
f->upvalues[i].idx = LoadByte(S); f->upvalues[i].idx = LoadByte(S);
f->upvalues[i].ro = LoadByte(S); f->upvalues[i].kind = LoadByte(S);
} }
} }

View File

@ -223,7 +223,7 @@ In Lua, the global variable @Lid{_G} is initialized with this same value.
so changing its value will affect only your own code.) so changing its value will affect only your own code.)
When Lua loads a chunk, When Lua loads a chunk,
the default value for its @id{_ENV} upvalue the default value for its @id{_ENV} variable
is the global environment @seeF{load}. is the global environment @seeF{load}.
Therefore, by default, Therefore, by default,
free names in Lua code refer to entries in the global environment free names in Lua code refer to entries in the global environment
@ -233,7 +233,7 @@ and some functions there operate on that environment.
You can use @Lid{load} (or @Lid{loadfile}) You can use @Lid{load} (or @Lid{loadfile})
to load a chunk with a different environment. to load a chunk with a different environment.
(In C, you have to load the chunk and then change the value (In C, you have to load the chunk and then change the value
of its first upvalue.) of its first upvalue; see @See{lua_setupvalue}.)
} }
@ -1224,7 +1224,7 @@ As such, chunks can define local variables,
receive arguments, and return values. receive arguments, and return values.
Moreover, such anonymous function is compiled as in the Moreover, such anonymous function is compiled as in the
scope of an external local variable called @id{_ENV} @see{globalenv}. scope of an external local variable called @id{_ENV} @see{globalenv}.
The resulting function always has @id{_ENV} as its only upvalue, The resulting function always has @id{_ENV} as its only external variable,
even if it does not use that variable. even if it does not use that variable.
A chunk can be stored in a file or in a string inside the host program. A chunk can be stored in a file or in a string inside the host program.
@ -2241,8 +2241,8 @@ and so the second @id{x} refers to the outside variable.
Because of the @x{lexical scoping} rules, Because of the @x{lexical scoping} rules,
local variables can be freely accessed by functions local variables can be freely accessed by functions
defined inside their scope. defined inside their scope.
A local variable used by an inner function is called A local variable used by an inner function is called an @def{upvalue}
an @def{upvalue}, or @emphx{external local variable}, (or @emphx{external local variable}, or simply @emphx{external variable})
inside the inner function. inside the inner function.
Notice that each execution of a @Rw{local} statement Notice that each execution of a @Rw{local} statement
@ -4765,11 +4765,7 @@ and returns its name.
Returns @id{NULL} (and pushes nothing) Returns @id{NULL} (and pushes nothing)
when the index @id{n} is greater than the number of upvalues. when the index @id{n} is greater than the number of upvalues.
For @N{C functions}, this function uses the empty string @T{""} See @Lid{debug.getupvalue} for more information about upvalues.
as a name for all upvalues.
(For Lua functions,
upvalues are the external local variables that the function uses,
and that are consequently included in its closure.)
} }
@ -8485,6 +8481,8 @@ The first parameter or local variable has @N{index 1}, and so on,
following the order that they are declared in the code, following the order that they are declared in the code,
counting only the variables that are active counting only the variables that are active
in the current scope of the function. in the current scope of the function.
Compile-time constants may not appear in this listing,
if they were optimized away by the compiler.
Negative indices refer to vararg arguments; Negative indices refer to vararg arguments;
@num{-1} is the first vararg argument. @num{-1} is the first vararg argument.
The function returns @nil if there is no variable with the given index, The function returns @nil if there is no variable with the given index,
@ -8520,8 +8518,15 @@ This function returns the name and the value of the upvalue
with index @id{up} of the function @id{f}. with index @id{up} of the function @id{f}.
The function returns @nil if there is no upvalue with the given index. The function returns @nil if there is no upvalue with the given index.
Variable names starting with @Char{(} (open parenthesis) @C{)} (For Lua functions,
represent variables with no known names upvalues are the external local variables that the function uses,
and that are consequently included in its closure.)
For @N{C functions}, this function uses the empty string @T{""}
as a name for all upvalues.
Variable name @Char{?} (interrogation mark)
represents variables with no known names
(variables from chunks saved without debug information). (variables from chunks saved without debug information).
} }
@ -8626,6 +8631,8 @@ The function returns @nil if there is no upvalue
with the given index. with the given index.
Otherwise, it returns the name of the upvalue. Otherwise, it returns the name of the upvalue.
See @Lid{debug.getupvalue} for more information about upvalues.
} }
@LibEntry{debug.setuservalue (udata, value, n)| @LibEntry{debug.setuservalue (udata, value, n)|

View File

@ -7,6 +7,22 @@ if T==nil then
end end
print "testing code generation and optimizations" print "testing code generation and optimizations"
-- to test constant propagation
local <const> k0 = 0
local <const> k1 = 1
local <const> k3 = 3
local <const> k6 = k3 + (k3 << k0)
local <const> kFF0 = 0xFF0
local <const> k3_78 = 3.78
local <const> x, <const> k3_78_4 = 10, k3_78 / 4
assert(x == 10)
local <const> kx = "x"
local <const> kTrue = true
local <const> kFalse = false
local <const> kNil = nil
-- this code gave an error for the code checker -- this code gave an error for the code checker
do do
@ -27,12 +43,12 @@ end
local function foo () local function foo ()
local a local a
a = 3; a = k3;
a = 0; a = 0.0; a = -7 + 7 a = 0; a = 0.0; a = -7 + 7
a = 3.78/4; a = 3.78/4 a = k3_78/4; a = k3_78_4
a = -3.78/4; a = 3.78/4; a = -3.78/4 a = -k3_78/4; a = k3_78/4; a = -3.78/4
a = -3.79/4; a = 0.0; a = -0; a = -3.79/4; a = 0.0; a = -0;
a = 3; a = 3.0; a = 3; a = 3.0 a = k3; a = 3.0; a = 3; a = 3.0
end end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4}) checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
@ -86,10 +102,11 @@ end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
-- sequence of LOADNILs -- sequence of LOADNILs
check(function () check(function ()
local <const> kNil = nil
local a,b,c local a,b,c
local d; local e; local d; local e;
local f,g,h; local f,g,h;
d = nil; d=nil; b=nil; a=nil; c=nil; d = nil; d=nil; b=nil; a=kNil; c=nil;
end, 'LOADNIL', 'RETURN0') end, 'LOADNIL', 'RETURN0')
check(function () check(function ()
@ -109,7 +126,7 @@ check (function (a,b,c) return a end, 'RETURN1')
-- infinite loops -- infinite loops
check(function () while true do local a = -1 end end, check(function () while kTrue do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0') 'LOADI', 'JMP', 'RETURN0')
check(function () while 1 do local a = -1 end end, check(function () while 1 do local a = -1 end end,
@ -125,9 +142,9 @@ check(function (a,b,c,d) return a..b..c..d end,
-- not -- not
check(function () return not not nil end, 'LOADBOOL', 'RETURN1') check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
check(function () return not not false end, 'LOADBOOL', 'RETURN1') check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
check(function () return not not true end, 'LOADBOOL', 'RETURN1') check(function () return not not true end, 'LOADBOOL', 'RETURN1')
check(function () return not not 1 end, 'LOADBOOL', 'RETURN1') check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
-- direct access to locals -- direct access to locals
check(function () check(function ()
@ -144,7 +161,8 @@ end,
-- direct access to constants -- direct access to constants
check(function () check(function ()
local a,b local a,b
a.x = 3.2 local c = kNil
a[kx] = 3.2
a.x = b a.x = b
a[b] = 'x' a[b] = 'x'
end, end,
@ -152,8 +170,9 @@ end,
-- "get/set table" with numeric indices -- "get/set table" with numeric indices
check(function (a) check(function (a)
local <const> k255 = 255
a[1] = a[100] a[1] = a[100]
a[255] = a[256] a[k255] = a[256]
a[256] = 5 a[256] = 5
end, end,
'GETI', 'SETI', 'GETI', 'SETI',
@ -170,7 +189,7 @@ end,
check(function () check(function ()
local a,b local a,b
a[true] = false a[kTrue] = false
end, end,
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0') 'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
@ -238,37 +257,39 @@ local function checkF (func, val)
end end
checkF(function () return 0.0 end, 0.0) checkF(function () return 0.0 end, 0.0)
checkI(function () return 0 end, 0) checkI(function () return k0 end, 0)
checkI(function () return -0//1 end, 0) checkI(function () return -k0//1 end, 0)
checkK(function () return 3^-1 end, 1/3) checkK(function () return 3^-1 end, 1/3)
checkK(function () return (1 + 1)^(50 + 50) end, 2^100) checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0) checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
checkF(function () return (-3^0 + 5) // 3.0 end, 1.0) checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
checkI(function () return -3 % 5 end, 2) checkI(function () return -k3 % 5 end, 2)
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0) checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0) checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4) checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
checkI(function () return ~(~0xFF0 | 0xFF0) end, 0) checkI(function () return ~(~kFF0 | kFF0) end, 0)
checkI(function () return ~~-1024.0 end, -1024) checkI(function () return ~~-1024.0 end, -1024)
checkI(function () return ((100 << 6) << -4) >> 2 end, 100) checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535) -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
checkI(function () return 65535 end, sbx) local <const> border = 65535
checkI(function () return -65535 end, -sbx) checkI(function () return border end, sbx)
checkI(function () return 65536 end, sbx + 1) checkI(function () return -border end, -sbx)
checkK(function () return 65537 end, sbx + 2) checkI(function () return border + 1 end, sbx + 1)
checkK(function () return -65536 end, -(sbx + 1)) checkK(function () return border + 2 end, sbx + 2)
checkK(function () return -(border + 1) end, -(sbx + 1))
checkF(function () return 65535.0 end, sbx + 0.0) local <const> border = 65535.0
checkF(function () return -65535.0 end, -sbx + 0.0) checkF(function () return border end, sbx + 0.0)
checkF(function () return 65536.0 end, (sbx + 1.0)) checkF(function () return -border end, -sbx + 0.0)
checkK(function () return 65537.0 end, (sbx + 2.0)) checkF(function () return border + 1 end, (sbx + 1.0))
checkK(function () return -65536.0 end, -(sbx + 1.0)) checkK(function () return border + 2 end, (sbx + 2.0))
checkK(function () return -(border + 1) end, -(sbx + 1.0))
-- immediate operands -- immediate operands
checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1') checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'RETURN1')
checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1') checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1') checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1') checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
@ -276,7 +297,7 @@ checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWI', 'RETURN1')
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1') checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1') checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1') checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1') checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'RETURN1')
checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1') checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1') checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1') checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
@ -295,7 +316,7 @@ checkR(function (x) return x % (100.0 - 10) end, 91, 1.0, 'MODK', 'RETURN1')
-- no foldings (and immediate operands) -- no foldings (and immediate operands)
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1') check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1') check(function () return k3/0 end, 'LOADI', 'DIVI', 'RETURN1')
check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1') check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1') check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1') check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
@ -335,7 +356,7 @@ end,
do -- tests for table access in upvalues do -- tests for table access in upvalues
local t local t
check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP') check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
check(function (a) t[a()] = t[a()] end, check(function (a) t[a()] = t[a()] end,
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL', 'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
'GETUPVAL', 'GETTABLE', 'SETTABLE') 'GETUPVAL', 'GETTABLE', 'SETTABLE')
@ -379,6 +400,12 @@ function (a)
end end
) )
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)
checkequal(function () return 6 and true or nil end,
function () return k6 and kTrue or kNil end)
print 'OK' print 'OK'

View File

@ -287,7 +287,7 @@ a,b = F(nil)==nil; assert(a == true and b == nil)
------------------------------------------------------------------ ------------------------------------------------------------------
-- sometimes will be 0, sometimes will not... -- sometimes will be 0, sometimes will not...
_ENV.GLOB1 = math.floor(os.time()) % 2 _ENV.GLOB1 = math.random(0, 1)
-- basic expressions with their respective values -- basic expressions with their respective values
local basiccases = { local basiccases = {
@ -298,6 +298,26 @@ local basiccases = {
{"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1}, {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
} }
local prog
if _ENV.GLOB1 == 0 then
basiccases[2][1] = "F" -- constant false
prog = [[
local <const> F = false
if %s then IX = true end
return %s
]]
else
basiccases[4][1] = "k10" -- constant 10
prog = [[
local <const> k10 = 10
if %s then IX = true end
return %s
]]
end
print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')') print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
@ -337,8 +357,6 @@ cases[1] = basiccases
for i = 2, level do cases[i] = createcases(i) end for i = 2, level do cases[i] = createcases(i) end
print("+") print("+")
local prog = [[if %s then IX = true end; return %s]]
local i = 0 local i = 0
for n = 1, level do for n = 1, level do
for _, v in pairs(cases[n]) do for _, v in pairs(cases[n]) do

View File

@ -324,7 +324,7 @@ do
-- errors due to non-closable values -- errors due to non-closable values
local function foo () local function foo ()
local <toclose> x = 34 local <toclose> x = {}
end end
local stat, msg = pcall(foo) local stat, msg = pcall(foo)
assert(not stat and string.find(msg, "variable 'x'")) assert(not stat and string.find(msg, "variable 'x'"))

View File

@ -270,7 +270,7 @@ else
end end
do do
local NaN = 0/0 local <const> NaN = 0/0
assert(not (NaN < 0)) assert(not (NaN < 0))
assert(not (NaN > minint)) assert(not (NaN > minint))
assert(not (NaN <= -9)) assert(not (NaN <= -9))
@ -767,7 +767,8 @@ assert(a == '10' and b == '20')
do do
print("testing -0 and NaN") print("testing -0 and NaN")
local mz, z = -0.0, 0.0 local <const> mz = -0.0
local <const> z = 0.0
assert(mz == z) assert(mz == z)
assert(1/mz < 0 and 0 < 1/z) assert(1/mz < 0 and 0 < 1/z)
local a = {[mz] = 1} local a = {[mz] = 1}
@ -775,17 +776,18 @@ do
a[z] = 2 a[z] = 2
assert(a[z] == 2 and a[mz] == 2) assert(a[z] == 2 and a[mz] == 2)
local inf = math.huge * 2 + 1 local inf = math.huge * 2 + 1
mz, z = -1/inf, 1/inf local <const> mz = -1/inf
local <const> z = 1/inf
assert(mz == z) assert(mz == z)
assert(1/mz < 0 and 0 < 1/z) assert(1/mz < 0 and 0 < 1/z)
local NaN = inf - inf local <const> NaN = inf - inf
assert(NaN ~= NaN) assert(NaN ~= NaN)
assert(not (NaN < NaN)) assert(not (NaN < NaN))
assert(not (NaN <= NaN)) assert(not (NaN <= NaN))
assert(not (NaN > NaN)) assert(not (NaN > NaN))
assert(not (NaN >= NaN)) assert(not (NaN >= NaN))
assert(not (0 < NaN) and not (NaN < 0)) assert(not (0 < NaN) and not (NaN < 0))
local NaN1 = 0/0 local <const> NaN1 = 0/0
assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN)) assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
local a = {} local a = {}
assert(not pcall(rawset, a, NaN, 1)) assert(not pcall(rawset, a, NaN, 1))
@ -814,8 +816,8 @@ end
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6 -- the first call after seed 1007 should return 0x7a7040a5a323c9d6
do do
-- all computations assume at most 32-bit integers -- all computations assume at most 32-bit integers
local h = 0x7a7040a5 -- higher half local <const> h = 0x7a7040a5 -- higher half
local l = 0xa323c9d6 -- lower half local <const> l = 0xa323c9d6 -- lower half
math.randomseed(1007) math.randomseed(1007)
-- get the low 'intbits' of the 64-bit expected result -- get the low 'intbits' of the 64-bit expected result