mirror of https://github.com/rusefi/lua.git
First implementation of constant propagation
Local constant variables initialized with compile-time constants are optimized away from the code.
This commit is contained in:
parent
be8445d7e4
commit
f6aab3ec1f
56
lcode.c
56
lcode.c
|
@ -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
|
||||
** 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'.
|
||||
** 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) {
|
||||
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 */
|
||||
e->u.info = e->u.var.sidx;
|
||||
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.
|
||||
*/
|
||||
static void codenot (FuncState *fs, expdesc *e) {
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (e->k) {
|
||||
case VNIL: case VFALSE: {
|
||||
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) {
|
||||
static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
|
||||
luaK_dischargevars(fs, e);
|
||||
switch (op) {
|
||||
case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */
|
||||
if (constfolding(fs, op + LUA_OPUNM, e, &ef))
|
||||
|
|
1
lcode.h
1
lcode.h
|
@ -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,
|
||||
int B, int C, int k);
|
||||
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_nil (FuncState *fs, int from, int n);
|
||||
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
|
||||
|
|
2
ldump.c
2
ldump.c
|
@ -149,7 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
|
|||
for (i = 0; i < n; i++) {
|
||||
DumpByte(f->upvalues[i].instack, D);
|
||||
DumpByte(f->upvalues[i].idx, D);
|
||||
DumpByte(f->upvalues[i].ro, D);
|
||||
DumpByte(f->upvalues[i].kind, D);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -460,7 +460,7 @@ typedef struct Upvaldesc {
|
|||
TString *name; /* upvalue name (for debug information) */
|
||||
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 ro; /* true if upvalue is read-only (const) */
|
||||
lu_byte kind; /* kind of corresponding variable */
|
||||
} Upvaldesc;
|
||||
|
||||
|
||||
|
|
113
lparser.c
113
lparser.c
|
@ -170,15 +170,16 @@ static void codename (LexState *ls, expdesc *e) {
|
|||
** Register a new local variable in the active 'Proto' (for debug
|
||||
** information).
|
||||
*/
|
||||
static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) {
|
||||
static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
|
||||
Proto *f = fs->f;
|
||||
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");
|
||||
while (oldsize < f->sizelocvars)
|
||||
f->locvars[oldsize++].varname = NULL;
|
||||
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++;
|
||||
}
|
||||
|
||||
|
@ -191,16 +192,13 @@ static Vardesc *new_localvar (LexState *ls, TString *name) {
|
|||
FuncState *fs = ls->fs;
|
||||
Dyndata *dyd = ls->dyd;
|
||||
Vardesc *var;
|
||||
int reg = registerlocalvar(L, fs, name);
|
||||
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
|
||||
MAXVARS, "local variables");
|
||||
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
|
||||
dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
|
||||
var = &dyd->actvar.arr[dyd->actvar.n++];
|
||||
var->pidx = cast(short, reg);
|
||||
var->ro = 0;
|
||||
var->name = name;
|
||||
setnilvalue(var);
|
||||
var->vd.kind = VDKREG; /* default is a regular variable */
|
||||
var->vd.name = name;
|
||||
return var;
|
||||
}
|
||||
|
||||
|
@ -225,8 +223,8 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
|
|||
static int stacklevel (FuncState *fs, int nvar) {
|
||||
while (nvar > 0) {
|
||||
Vardesc *vd = getlocalvardesc(fs, nvar - 1);
|
||||
if (vdinstack(vd)) /* is in the stack? */
|
||||
return vd->sidx + 1;
|
||||
if (vd->vd.kind != RDKCTC) /* is in the stack? */
|
||||
return vd->vd.sidx + 1;
|
||||
else
|
||||
nvar--; /* try previous variable */
|
||||
}
|
||||
|
@ -247,10 +245,10 @@ int luaY_nvarstack (FuncState *fs) {
|
|||
*/
|
||||
static LocVar *localdebuginfo (FuncState *fs, int i) {
|
||||
Vardesc *vd = getlocalvardesc(fs, i);
|
||||
if (!vdinstack(vd))
|
||||
if (vd->vd.kind == RDKCTC)
|
||||
return NULL; /* no debug info. for constants */
|
||||
else {
|
||||
int idx = vd->pidx;
|
||||
int idx = vd->vd.pidx;
|
||||
lua_assert(idx < fs->ndebugvars);
|
||||
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->k = VLOCAL;
|
||||
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;
|
||||
TString *varname = NULL; /* to be set if variable is const */
|
||||
switch (e->k) {
|
||||
case VCONST: {
|
||||
varname = ls->dyd->actvar.arr[e->u.info].vd.name;
|
||||
break;
|
||||
}
|
||||
case VLOCAL: {
|
||||
Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
|
||||
if (vardesc->ro)
|
||||
varname = vardesc->name;
|
||||
if (vardesc->vd.kind != VDKREG) /* not a regular variable? */
|
||||
varname = vardesc->vd.name;
|
||||
break;
|
||||
}
|
||||
case VUPVAL: {
|
||||
Upvaldesc *up = &fs->f->upvalues[e->u.info];
|
||||
if (up->ro)
|
||||
if (up->kind != VDKREG)
|
||||
varname = up->name;
|
||||
break;
|
||||
}
|
||||
|
@ -302,8 +304,8 @@ static void adjustlocalvars (LexState *ls, int nvars) {
|
|||
for (i = 0; i < nvars; i++) {
|
||||
int varidx = fs->nactvar++;
|
||||
Vardesc *var = getlocalvardesc(fs, varidx);
|
||||
var->sidx = stklevel++;
|
||||
fs->f->locvars[var->pidx].startpc = fs->pc;
|
||||
var->vd.sidx = stklevel++;
|
||||
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) {
|
||||
up->instack = 1;
|
||||
up->idx = v->u.var.sidx;
|
||||
up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro;
|
||||
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name));
|
||||
up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
|
||||
lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
|
||||
}
|
||||
else {
|
||||
up->instack = 0;
|
||||
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));
|
||||
}
|
||||
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
|
||||
** function 'fs'.
|
||||
*/
|
||||
static int searchvar (FuncState *fs, TString *n) {
|
||||
static int searchvar (FuncState *fs, TString *n, expdesc *var) {
|
||||
int i;
|
||||
for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
|
||||
if (eqstr(n, getlocalvardesc(fs, i)->name))
|
||||
return i;
|
||||
Vardesc *vd = getlocalvardesc(fs, 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 */
|
||||
}
|
||||
|
@ -405,20 +413,19 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
|
|||
if (fs == NULL) /* no more levels? */
|
||||
init_exp(var, VVOID, 0); /* default is global */
|
||||
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? */
|
||||
init_var(fs, var, v); /* variable is local */
|
||||
if (!base)
|
||||
if (v == VLOCAL && !base)
|
||||
markupval(fs, var->u.var.vidx); /* local will be used as an upval */
|
||||
}
|
||||
else { /* not found as local at current level; try upvalues */
|
||||
int idx = searchupvalue(fs, n); /* try existing upvalues */
|
||||
if (idx < 0) { /* not found? */
|
||||
singlevaraux(fs->prev, n, var, 0); /* try upper levels */
|
||||
if (var->k == VVOID) /* not found? */
|
||||
return; /* it is a global */
|
||||
/* else was LOCAL or UPVAL */
|
||||
idx = newupvalue(fs, n, var); /* will be a new upvalue */
|
||||
if (var->k == VLOCAL || var->k == VUPVAL) /* local or 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 */
|
||||
}
|
||||
|
@ -483,7 +490,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
|
|||
** local variable.
|
||||
*/
|
||||
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'";
|
||||
msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
|
||||
luaK_semerror(ls, msg); /* raise the error */
|
||||
|
@ -1710,21 +1717,20 @@ static int getlocalattribute (LexState *ls) {
|
|||
const char *attr = getstr(str_checkname(ls));
|
||||
checknext(ls, '>');
|
||||
if (strcmp(attr, "const") == 0)
|
||||
return 1; /* read-only variable */
|
||||
return RDKCONST; /* read-only variable */
|
||||
else if (strcmp(attr, "toclose") == 0)
|
||||
return 2; /* to-be-closed variable */
|
||||
return RDKTOCLOSE; /* to-be-closed variable */
|
||||
else
|
||||
luaK_semerror(ls,
|
||||
luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
|
||||
}
|
||||
return 0;
|
||||
return VDKREG;
|
||||
}
|
||||
|
||||
|
||||
static void checktoclose (LexState *ls, int toclose) {
|
||||
if (toclose != -1) { /* is there a to-be-closed variable? */
|
||||
static void checktoclose (LexState *ls, int level) {
|
||||
if (level != -1) { /* is there a to-be-closed variable? */
|
||||
FuncState *fs = ls->fs;
|
||||
int level = luaY_nvarstack(fs) + toclose;
|
||||
markupval(fs, level + 1);
|
||||
fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */
|
||||
luaK_codeABC(fs, OP_TBC, level, 0, 0);
|
||||
|
@ -1734,20 +1740,20 @@ static void checktoclose (LexState *ls, int toclose) {
|
|||
|
||||
static void localstat (LexState *ls) {
|
||||
/* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
|
||||
FuncState *fs = ls->fs;
|
||||
int toclose = -1; /* index of to-be-closed variable (if any) */
|
||||
Vardesc *var; /* last variable */
|
||||
int nvars = 0;
|
||||
int nexps;
|
||||
expdesc e;
|
||||
do {
|
||||
int kind = getlocalattribute(ls);
|
||||
Vardesc *var = new_localvar(ls, str_checkname(ls));
|
||||
if (kind != 0) { /* is there an attribute? */
|
||||
var->ro = 1; /* all attributes make variable read-only */
|
||||
if (kind == 2) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||
toclose = nvars;
|
||||
}
|
||||
var = new_localvar(ls, str_checkname(ls));
|
||||
var->vd.kind = kind;
|
||||
if (kind == RDKTOCLOSE) { /* to-be-closed? */
|
||||
if (toclose != -1) /* one already present? */
|
||||
luaK_semerror(ls, "multiple to-be-closed variables in local list");
|
||||
toclose = luaY_nvarstack(fs) + nvars;
|
||||
}
|
||||
nvars++;
|
||||
} while (testnext(ls, ','));
|
||||
|
@ -1757,9 +1763,18 @@ static void localstat (LexState *ls) {
|
|||
e.k = VVOID;
|
||||
nexps = 0;
|
||||
}
|
||||
adjust_assign(ls, nvars, nexps, &e);
|
||||
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);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
checktoclose(ls, toclose);
|
||||
adjustlocalvars(ls, nvars);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1925,7 +1940,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
|
|||
env = allocupvalue(fs); /* ...set environment upvalue */
|
||||
env->instack = 1;
|
||||
env->idx = 0;
|
||||
env->ro = 0;
|
||||
env->kind = VDKREG;
|
||||
env->name = ls->envn;
|
||||
luaX_next(ls); /* read first token */
|
||||
statlist(ls); /* parse main body */
|
||||
|
|
27
lparser.h
27
lparser.h
|
@ -34,8 +34,9 @@ typedef enum {
|
|||
VNONRELOC, /* expression has its value in a fixed register;
|
||||
info = result 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' */
|
||||
VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */
|
||||
VINDEXED, /* indexed variable;
|
||||
ind.t = table register;
|
||||
ind.idx = key's R index */
|
||||
|
@ -81,19 +82,25 @@ typedef struct 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 */
|
||||
typedef struct Vardesc {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
lu_byte ro; /* true if variable is 'const' */
|
||||
lu_byte sidx; /* index of the variable in the stack */
|
||||
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||
TString *name; /* variable name */
|
||||
typedef union Vardesc {
|
||||
struct {
|
||||
TValuefields; /* constant value (if it is a compile-time constant) */
|
||||
lu_byte kind;
|
||||
lu_byte sidx; /* index of the variable in the stack */
|
||||
short pidx; /* index of the variable in the Proto's 'locvars' array */
|
||||
TString *name; /* variable name */
|
||||
} vd;
|
||||
TValue k; /* constant value (if any) */
|
||||
} 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 */
|
||||
typedef struct Labeldesc {
|
||||
|
|
|
@ -198,12 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
|
|||
n = LoadInt(S);
|
||||
f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
|
||||
f->sizeupvalues = n;
|
||||
for (i = 0; i < n; i++)
|
||||
f->upvalues[i].name = NULL;
|
||||
for (i = 0; i < n; i++) {
|
||||
f->upvalues[i].name = NULL;
|
||||
f->upvalues[i].instack = LoadByte(S);
|
||||
f->upvalues[i].idx = LoadByte(S);
|
||||
f->upvalues[i].ro = LoadByte(S);
|
||||
f->upvalues[i].kind = LoadByte(S);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.)
|
||||
|
||||
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}.
|
||||
Therefore, by default,
|
||||
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})
|
||||
to load a chunk with a different environment.
|
||||
(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.
|
||||
Moreover, such anonymous function is compiled as in the
|
||||
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.
|
||||
|
||||
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,
|
||||
local variables can be freely accessed by functions
|
||||
defined inside their scope.
|
||||
A local variable used by an inner function is called
|
||||
an @def{upvalue}, or @emphx{external local variable},
|
||||
A local variable used by an inner function is called an @def{upvalue}
|
||||
(or @emphx{external local variable}, or simply @emphx{external variable})
|
||||
inside the inner function.
|
||||
|
||||
Notice that each execution of a @Rw{local} statement
|
||||
|
@ -4765,11 +4765,7 @@ and returns its name.
|
|||
Returns @id{NULL} (and pushes nothing)
|
||||
when the index @id{n} is greater than the number of upvalues.
|
||||
|
||||
For @N{C functions}, this function uses the empty string @T{""}
|
||||
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.)
|
||||
See @Lid{debug.getupvalue} for more information about upvalues.
|
||||
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
counting only the variables that are active
|
||||
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;
|
||||
@num{-1} is the first vararg argument.
|
||||
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}.
|
||||
The function returns @nil if there is no upvalue with the given index.
|
||||
|
||||
Variable names starting with @Char{(} (open parenthesis) @C{)}
|
||||
represent variables with no known names
|
||||
(For Lua functions,
|
||||
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).
|
||||
|
||||
}
|
||||
|
@ -8626,6 +8631,8 @@ The function returns @nil if there is no upvalue
|
|||
with the given index.
|
||||
Otherwise, it returns the name of the upvalue.
|
||||
|
||||
See @Lid{debug.getupvalue} for more information about upvalues.
|
||||
|
||||
}
|
||||
|
||||
@LibEntry{debug.setuservalue (udata, value, n)|
|
||||
|
|
|
@ -7,6 +7,22 @@ if T==nil then
|
|||
end
|
||||
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
|
||||
do
|
||||
|
@ -27,12 +43,12 @@ end
|
|||
|
||||
local function foo ()
|
||||
local a
|
||||
a = 3;
|
||||
a = k3;
|
||||
a = 0; a = 0.0; a = -7 + 7
|
||||
a = 3.78/4; a = 3.78/4
|
||||
a = -3.78/4; a = 3.78/4; a = -3.78/4
|
||||
a = k3_78/4; a = k3_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; a = 3.0; a = 3; a = 3.0
|
||||
a = k3; a = 3.0; a = 3; a = 3.0
|
||||
end
|
||||
|
||||
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
|
||||
check(function ()
|
||||
local <const> kNil = nil
|
||||
local a,b,c
|
||||
local d; local e;
|
||||
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')
|
||||
|
||||
check(function ()
|
||||
|
@ -109,7 +126,7 @@ check (function (a,b,c) return a end, 'RETURN1')
|
|||
|
||||
|
||||
-- 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')
|
||||
|
||||
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
|
||||
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 1 end, 'LOADBOOL', 'RETURN1')
|
||||
check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
|
||||
|
||||
-- direct access to locals
|
||||
check(function ()
|
||||
|
@ -144,7 +161,8 @@ end,
|
|||
-- direct access to constants
|
||||
check(function ()
|
||||
local a,b
|
||||
a.x = 3.2
|
||||
local c = kNil
|
||||
a[kx] = 3.2
|
||||
a.x = b
|
||||
a[b] = 'x'
|
||||
end,
|
||||
|
@ -152,8 +170,9 @@ end,
|
|||
|
||||
-- "get/set table" with numeric indices
|
||||
check(function (a)
|
||||
local <const> k255 = 255
|
||||
a[1] = a[100]
|
||||
a[255] = a[256]
|
||||
a[k255] = a[256]
|
||||
a[256] = 5
|
||||
end,
|
||||
'GETI', 'SETI',
|
||||
|
@ -170,7 +189,7 @@ end,
|
|||
|
||||
check(function ()
|
||||
local a,b
|
||||
a[true] = false
|
||||
a[kTrue] = false
|
||||
end,
|
||||
'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
|
||||
|
||||
|
@ -238,37 +257,39 @@ local function checkF (func, val)
|
|||
end
|
||||
|
||||
checkF(function () return 0.0 end, 0.0)
|
||||
checkI(function () return 0 end, 0)
|
||||
checkI(function () return -0//1 end, 0)
|
||||
checkI(function () return k0 end, 0)
|
||||
checkI(function () return -k0//1 end, 0)
|
||||
checkK(function () return 3^-1 end, 1/3)
|
||||
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
|
||||
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
|
||||
checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
|
||||
checkI(function () return -3 % 5 end, 2)
|
||||
checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
|
||||
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^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
|
||||
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 ((100 << 6) << -4) >> 2 end, 100)
|
||||
checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
|
||||
|
||||
-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
|
||||
local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding
|
||||
checkI(function () return 65535 end, sbx)
|
||||
checkI(function () return -65535 end, -sbx)
|
||||
checkI(function () return 65536 end, sbx + 1)
|
||||
checkK(function () return 65537 end, sbx + 2)
|
||||
checkK(function () return -65536 end, -(sbx + 1))
|
||||
local <const> border = 65535
|
||||
checkI(function () return border end, sbx)
|
||||
checkI(function () return -border end, -sbx)
|
||||
checkI(function () return border + 1 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)
|
||||
checkF(function () return -65535.0 end, -sbx + 0.0)
|
||||
checkF(function () return 65536.0 end, (sbx + 1.0))
|
||||
checkK(function () return 65537.0 end, (sbx + 2.0))
|
||||
checkK(function () return -65536.0 end, -(sbx + 1.0))
|
||||
local <const> border = 65535.0
|
||||
checkF(function () return border end, sbx + 0.0)
|
||||
checkF(function () return -border end, -sbx + 0.0)
|
||||
checkF(function () return border + 1 end, (sbx + 1.0))
|
||||
checkK(function () return border + 2 end, (sbx + 2.0))
|
||||
checkK(function () return -(border + 1) end, -(sbx + 1.0))
|
||||
|
||||
|
||||
-- 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 x * -127 end, -1.0, 127.0, '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 // 1 end, 10.0, 10.0, 'IDIVI', '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, 8, 2, 'SHRI', '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)
|
||||
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 -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
|
||||
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
|
||||
|
@ -335,7 +356,7 @@ end,
|
|||
|
||||
do -- tests for table access in upvalues
|
||||
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,
|
||||
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
|
||||
'GETUPVAL', 'GETTABLE', 'SETTABLE')
|
||||
|
@ -379,6 +400,12 @@ function (a)
|
|||
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'
|
||||
|
||||
|
|
|
@ -287,7 +287,7 @@ a,b = F(nil)==nil; assert(a == true and b == nil)
|
|||
------------------------------------------------------------------
|
||||
|
||||
-- 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
|
||||
local basiccases = {
|
||||
|
@ -298,6 +298,26 @@ local basiccases = {
|
|||
{"(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 .. ')')
|
||||
|
||||
|
||||
|
@ -337,8 +357,6 @@ cases[1] = basiccases
|
|||
for i = 2, level do cases[i] = createcases(i) end
|
||||
print("+")
|
||||
|
||||
local prog = [[if %s then IX = true end; return %s]]
|
||||
|
||||
local i = 0
|
||||
for n = 1, level do
|
||||
for _, v in pairs(cases[n]) do
|
||||
|
|
|
@ -324,7 +324,7 @@ do
|
|||
|
||||
-- errors due to non-closable values
|
||||
local function foo ()
|
||||
local <toclose> x = 34
|
||||
local <toclose> x = {}
|
||||
end
|
||||
local stat, msg = pcall(foo)
|
||||
assert(not stat and string.find(msg, "variable 'x'"))
|
||||
|
|
|
@ -270,7 +270,7 @@ else
|
|||
end
|
||||
|
||||
do
|
||||
local NaN = 0/0
|
||||
local <const> NaN = 0/0
|
||||
assert(not (NaN < 0))
|
||||
assert(not (NaN > minint))
|
||||
assert(not (NaN <= -9))
|
||||
|
@ -767,7 +767,8 @@ assert(a == '10' and b == '20')
|
|||
|
||||
do
|
||||
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(1/mz < 0 and 0 < 1/z)
|
||||
local a = {[mz] = 1}
|
||||
|
@ -775,17 +776,18 @@ do
|
|||
a[z] = 2
|
||||
assert(a[z] == 2 and a[mz] == 2)
|
||||
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(1/mz < 0 and 0 < 1/z)
|
||||
local NaN = inf - inf
|
||||
local <const> NaN = inf - inf
|
||||
assert(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))
|
||||
local NaN1 = 0/0
|
||||
local <const> NaN1 = 0/0
|
||||
assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
|
||||
local a = {}
|
||||
assert(not pcall(rawset, a, NaN, 1))
|
||||
|
@ -814,8 +816,8 @@ end
|
|||
-- the first call after seed 1007 should return 0x7a7040a5a323c9d6
|
||||
do
|
||||
-- all computations assume at most 32-bit integers
|
||||
local h = 0x7a7040a5 -- higher half
|
||||
local l = 0xa323c9d6 -- lower half
|
||||
local <const> h = 0x7a7040a5 -- higher half
|
||||
local <const> l = 0xa323c9d6 -- lower half
|
||||
|
||||
math.randomseed(1007)
|
||||
-- get the low 'intbits' of the 64-bit expected result
|
||||
|
|
Loading…
Reference in New Issue