summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-07-12 11:38:42 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-07-12 11:38:42 -0300
commitf6aab3ec1f111cd8d968bdcb7ca800e93b819d24 (patch)
tree4c36c418ecc9062e6d95de73457198b38b0afce9
parentbe8445d7e4b6122620c428877b51a27d464253d5 (diff)
downloadlua-github-f6aab3ec1f111cd8d968bdcb7ca800e93b819d24.tar.gz
First implementation of constant propagation
Local constant variables initialized with compile-time constants are optimized away from the code.
-rw-r--r--lcode.c56
-rw-r--r--lcode.h1
-rw-r--r--ldump.c2
-rw-r--r--lobject.h2
-rw-r--r--lparser.c113
-rw-r--r--lparser.h27
-rw-r--r--lundump.c5
-rw-r--r--manual/manual.of31
-rw-r--r--testes/code.lua89
-rw-r--r--testes/constructs.lua24
-rw-r--r--testes/locals.lua2
-rw-r--r--testes/math.lua16
12 files changed, 249 insertions, 119 deletions
diff --git a/lcode.c b/lcode.c
index 837253f4..74ff47de 100644
--- a/lcode.c
+++ b/lcode.c
@@ -68,6 +68,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
** previous one, return an invalid instruction (to avoid wrong
@@ -630,6 +654,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)
** or 'nresults' is LUA_MULTRET (as any expression can satisfy that).
@@ -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))
diff --git a/lcode.h b/lcode.h
index 0758f88d..a15b6875 100644
--- a/lcode.h
+++ b/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);
diff --git a/ldump.c b/ldump.c
index 3d5b7b32..9b501729 100644
--- a/ldump.c
+++ b/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);
}
}
diff --git a/lobject.h b/lobject.h
index 64366a94..2f95bcb5 100644
--- a/lobject.h
+++ b/lobject.h
@@ -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;
diff --git a/lparser.c b/lparser.c
index 7f282bf9..79df0217 100644
--- a/lparser.c
+++ b/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 */
diff --git a/lparser.h b/lparser.h
index 7d43a813..d9b734bf 100644
--- a/lparser.h
+++ b/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 {
diff --git a/lundump.c b/lundump.c
index 5c0e94d6..8f2a490c 100644
--- a/lundump.c
+++ b/lundump.c
@@ -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);
}
}
diff --git a/manual/manual.of b/manual/manual.of
index 136e9022..61fcdaa3 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -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)|
diff --git a/testes/code.lua b/testes/code.lua
index 128ca2cb..49d682f8 100644
--- a/testes/code.lua
+++ b/testes/code.lua
@@ -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'
diff --git a/testes/constructs.lua b/testes/constructs.lua
index fe4db2cb..8a549e10 100644
--- a/testes/constructs.lua
+++ b/testes/constructs.lua
@@ -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
diff --git a/testes/locals.lua b/testes/locals.lua
index 0de00a98..1b82dd7f 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -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'"))
diff --git a/testes/math.lua b/testes/math.lua
index c45a91ad..befce12e 100644
--- a/testes/math.lua
+++ b/testes/math.lua
@@ -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