diff --git a/lcode.c b/lcode.c index 837253f4..74ff47de 100644 --- a/lcode.c +++ b/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)) 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 = " 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 k0 = 0 +local k1 = 1 +local k3 = 3 +local k6 = k3 + (k3 << k0) +local kFF0 = 0xFF0 +local k3_78 = 3.78 +local x, k3_78_4 = 10, k3_78 / 4 +assert(x == 10) + +local kx = "x" + +local kTrue = true +local kFalse = false + +local 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 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 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 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 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 F = false + if %s then IX = true end + return %s +]] +else + basiccases[4][1] = "k10" -- constant 10 + + prog = [[ + local 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 x = 34 + local 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 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 mz = -0.0 + local 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 mz = -1/inf + local z = 1/inf assert(mz == z) assert(1/mz < 0 and 0 < 1/z) - local NaN = inf - inf + local 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 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 h = 0x7a7040a5 -- higher half + local l = 0xa323c9d6 -- lower half math.randomseed(1007) -- get the low 'intbits' of the 64-bit expected result