diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-12 11:38:42 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-12 11:38:42 -0300 |
commit | f6aab3ec1f111cd8d968bdcb7ca800e93b819d24 (patch) | |
tree | 4c36c418ecc9062e6d95de73457198b38b0afce9 | |
parent | be8445d7e4b6122620c428877b51a27d464253d5 (diff) | |
download | lua-f6aab3ec1f111cd8d968bdcb7ca800e93b819d24.tar.gz lua-f6aab3ec1f111cd8d968bdcb7ca800e93b819d24.tar.bz2 lua-f6aab3ec1f111cd8d968bdcb7ca800e93b819d24.zip |
First implementation of constant propagation
Local constant variables initialized with compile-time constants
are optimized away from the code.
-rw-r--r-- | lcode.c | 56 | ||||
-rw-r--r-- | lcode.h | 1 | ||||
-rw-r--r-- | ldump.c | 2 | ||||
-rw-r--r-- | lobject.h | 2 | ||||
-rw-r--r-- | lparser.c | 113 | ||||
-rw-r--r-- | lparser.h | 27 | ||||
-rw-r--r-- | lundump.c | 5 | ||||
-rw-r--r-- | manual/manual.of | 31 | ||||
-rw-r--r-- | testes/code.lua | 89 | ||||
-rw-r--r-- | testes/constructs.lua | 24 | ||||
-rw-r--r-- | testes/locals.lua | 2 | ||||
-rw-r--r-- | testes/math.lua | 16 |
12 files changed, 249 insertions, 119 deletions
@@ -68,6 +68,30 @@ static int tonumeral (const expdesc *e, TValue *v) { | |||
68 | 68 | ||
69 | 69 | ||
70 | /* | 70 | /* |
71 | ** If expression is a constant, fills 'v' with its value | ||
72 | ** and returns 1. Otherwise, returns 0. | ||
73 | */ | ||
74 | int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) { | ||
75 | if (hasjumps(e)) | ||
76 | return 0; /* not a constant */ | ||
77 | switch (e->k) { | ||
78 | case VFALSE: case VTRUE: | ||
79 | setbvalue(v, e->k == VTRUE); | ||
80 | return 1; | ||
81 | case VNIL: | ||
82 | setnilvalue(v); | ||
83 | return 1; | ||
84 | case VK: { | ||
85 | TValue *k = &fs->f->k[e->u.info]; | ||
86 | setobj(fs->ls->L, v, k); | ||
87 | return 1; | ||
88 | } | ||
89 | default: return tonumeral(e, v); | ||
90 | } | ||
91 | } | ||
92 | |||
93 | |||
94 | /* | ||
71 | ** Return the previous instruction of the current code. If there | 95 | ** Return the previous instruction of the current code. If there |
72 | ** may be a jump target between the current instruction and the | 96 | ** may be a jump target between the current instruction and the |
73 | ** previous one, return an invalid instruction (to avoid wrong | 97 | ** previous one, return an invalid instruction (to avoid wrong |
@@ -630,6 +654,31 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) { | |||
630 | 654 | ||
631 | 655 | ||
632 | /* | 656 | /* |
657 | ** Convert a constant in 'v' into an expression description 'e' | ||
658 | */ | ||
659 | static void const2exp (FuncState *fs, TValue *v, expdesc *e) { | ||
660 | switch (ttypetag(v)) { | ||
661 | case LUA_TNUMINT: | ||
662 | e->k = VKINT; e->u.ival = ivalue(v); | ||
663 | break; | ||
664 | case LUA_TNUMFLT: | ||
665 | e->k = VKFLT; e->u.nval = fltvalue(v); | ||
666 | break; | ||
667 | case LUA_TBOOLEAN: | ||
668 | e->k = bvalue(v) ? VTRUE : VFALSE; | ||
669 | break; | ||
670 | case LUA_TNIL: | ||
671 | e->k = VNIL; | ||
672 | break; | ||
673 | case LUA_TSHRSTR: case LUA_TLNGSTR: | ||
674 | e->k = VK; e->u.info = luaK_stringK(fs, tsvalue(v)); | ||
675 | break; | ||
676 | default: lua_assert(0); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | |||
681 | /* | ||
633 | ** Fix an expression to return the number of results 'nresults'. | 682 | ** Fix an expression to return the number of results 'nresults'. |
634 | ** Either 'e' is a multi-ret expression (function call or vararg) | 683 | ** Either 'e' is a multi-ret expression (function call or vararg) |
635 | ** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). | 684 | ** or 'nresults' is LUA_MULTRET (as any expression can satisfy that). |
@@ -677,6 +726,11 @@ void luaK_setoneret (FuncState *fs, expdesc *e) { | |||
677 | */ | 726 | */ |
678 | void luaK_dischargevars (FuncState *fs, expdesc *e) { | 727 | void luaK_dischargevars (FuncState *fs, expdesc *e) { |
679 | switch (e->k) { | 728 | switch (e->k) { |
729 | case VCONST: { | ||
730 | TValue *val = &fs->ls->dyd->actvar.arr[e->u.info].k; | ||
731 | const2exp(fs, val, e); | ||
732 | break; | ||
733 | } | ||
680 | case VLOCAL: { /* already in a register */ | 734 | case VLOCAL: { /* already in a register */ |
681 | e->u.info = e->u.var.sidx; | 735 | e->u.info = e->u.var.sidx; |
682 | e->k = VNONRELOC; /* becomes a non-relocatable value */ | 736 | e->k = VNONRELOC; /* becomes a non-relocatable value */ |
@@ -1074,7 +1128,6 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) { | |||
1074 | ** Code 'not e', doing constant folding. | 1128 | ** Code 'not e', doing constant folding. |
1075 | */ | 1129 | */ |
1076 | static void codenot (FuncState *fs, expdesc *e) { | 1130 | static void codenot (FuncState *fs, expdesc *e) { |
1077 | luaK_dischargevars(fs, e); | ||
1078 | switch (e->k) { | 1131 | switch (e->k) { |
1079 | case VNIL: case VFALSE: { | 1132 | case VNIL: case VFALSE: { |
1080 | e->k = VTRUE; /* true == not nil == not false */ | 1133 | e->k = VTRUE; /* true == not nil == not false */ |
@@ -1447,6 +1500,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) { | |||
1447 | */ | 1500 | */ |
1448 | void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { | 1501 | void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) { |
1449 | static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; | 1502 | static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP}; |
1503 | luaK_dischargevars(fs, e); | ||
1450 | switch (op) { | 1504 | switch (op) { |
1451 | case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ | 1505 | case OPR_MINUS: case OPR_BNOT: /* use 'ef' as fake 2nd operand */ |
1452 | if (constfolding(fs, op + LUA_OPUNM, e, &ef)) | 1506 | if (constfolding(fs, op + LUA_OPUNM, e, &ef)) |
@@ -56,6 +56,7 @@ LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx); | |||
56 | LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, | 56 | LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A, |
57 | int B, int C, int k); | 57 | int B, int C, int k); |
58 | LUAI_FUNC int luaK_isKint (expdesc *e); | 58 | LUAI_FUNC int luaK_isKint (expdesc *e); |
59 | LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v); | ||
59 | LUAI_FUNC void luaK_fixline (FuncState *fs, int line); | 60 | LUAI_FUNC void luaK_fixline (FuncState *fs, int line); |
60 | LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); | 61 | LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); |
61 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); | 62 | LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); |
@@ -149,7 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) { | |||
149 | for (i = 0; i < n; i++) { | 149 | for (i = 0; i < n; i++) { |
150 | DumpByte(f->upvalues[i].instack, D); | 150 | DumpByte(f->upvalues[i].instack, D); |
151 | DumpByte(f->upvalues[i].idx, D); | 151 | DumpByte(f->upvalues[i].idx, D); |
152 | DumpByte(f->upvalues[i].ro, D); | 152 | DumpByte(f->upvalues[i].kind, D); |
153 | } | 153 | } |
154 | } | 154 | } |
155 | 155 | ||
@@ -460,7 +460,7 @@ typedef struct Upvaldesc { | |||
460 | TString *name; /* upvalue name (for debug information) */ | 460 | TString *name; /* upvalue name (for debug information) */ |
461 | lu_byte instack; /* whether it is in stack (register) */ | 461 | lu_byte instack; /* whether it is in stack (register) */ |
462 | lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ | 462 | lu_byte idx; /* index of upvalue (in stack or in outer function's list) */ |
463 | lu_byte ro; /* true if upvalue is read-only (const) */ | 463 | lu_byte kind; /* kind of corresponding variable */ |
464 | } Upvaldesc; | 464 | } Upvaldesc; |
465 | 465 | ||
466 | 466 | ||
@@ -170,15 +170,16 @@ static void codename (LexState *ls, expdesc *e) { | |||
170 | ** Register a new local variable in the active 'Proto' (for debug | 170 | ** Register a new local variable in the active 'Proto' (for debug |
171 | ** information). | 171 | ** information). |
172 | */ | 172 | */ |
173 | static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) { | 173 | static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) { |
174 | Proto *f = fs->f; | 174 | Proto *f = fs->f; |
175 | int oldsize = f->sizelocvars; | 175 | int oldsize = f->sizelocvars; |
176 | luaM_growvector(L, f->locvars, fs->ndebugvars, f->sizelocvars, | 176 | luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars, |
177 | LocVar, SHRT_MAX, "local variables"); | 177 | LocVar, SHRT_MAX, "local variables"); |
178 | while (oldsize < f->sizelocvars) | 178 | while (oldsize < f->sizelocvars) |
179 | f->locvars[oldsize++].varname = NULL; | 179 | f->locvars[oldsize++].varname = NULL; |
180 | f->locvars[fs->ndebugvars].varname = varname; | 180 | f->locvars[fs->ndebugvars].varname = varname; |
181 | luaC_objbarrier(L, f, varname); | 181 | f->locvars[fs->ndebugvars].startpc = fs->pc; |
182 | luaC_objbarrier(ls->L, f, varname); | ||
182 | return fs->ndebugvars++; | 183 | return fs->ndebugvars++; |
183 | } | 184 | } |
184 | 185 | ||
@@ -191,16 +192,13 @@ static Vardesc *new_localvar (LexState *ls, TString *name) { | |||
191 | FuncState *fs = ls->fs; | 192 | FuncState *fs = ls->fs; |
192 | Dyndata *dyd = ls->dyd; | 193 | Dyndata *dyd = ls->dyd; |
193 | Vardesc *var; | 194 | Vardesc *var; |
194 | int reg = registerlocalvar(L, fs, name); | ||
195 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, | 195 | checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal, |
196 | MAXVARS, "local variables"); | 196 | MAXVARS, "local variables"); |
197 | luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, | 197 | luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1, |
198 | dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); | 198 | dyd->actvar.size, Vardesc, USHRT_MAX, "local variables"); |
199 | var = &dyd->actvar.arr[dyd->actvar.n++]; | 199 | var = &dyd->actvar.arr[dyd->actvar.n++]; |
200 | var->pidx = cast(short, reg); | 200 | var->vd.kind = VDKREG; /* default is a regular variable */ |
201 | var->ro = 0; | 201 | var->vd.name = name; |
202 | var->name = name; | ||
203 | setnilvalue(var); | ||
204 | return var; | 202 | return var; |
205 | } | 203 | } |
206 | 204 | ||
@@ -225,8 +223,8 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) { | |||
225 | static int stacklevel (FuncState *fs, int nvar) { | 223 | static int stacklevel (FuncState *fs, int nvar) { |
226 | while (nvar > 0) { | 224 | while (nvar > 0) { |
227 | Vardesc *vd = getlocalvardesc(fs, nvar - 1); | 225 | Vardesc *vd = getlocalvardesc(fs, nvar - 1); |
228 | if (vdinstack(vd)) /* is in the stack? */ | 226 | if (vd->vd.kind != RDKCTC) /* is in the stack? */ |
229 | return vd->sidx + 1; | 227 | return vd->vd.sidx + 1; |
230 | else | 228 | else |
231 | nvar--; /* try previous variable */ | 229 | nvar--; /* try previous variable */ |
232 | } | 230 | } |
@@ -247,10 +245,10 @@ int luaY_nvarstack (FuncState *fs) { | |||
247 | */ | 245 | */ |
248 | static LocVar *localdebuginfo (FuncState *fs, int i) { | 246 | static LocVar *localdebuginfo (FuncState *fs, int i) { |
249 | Vardesc *vd = getlocalvardesc(fs, i); | 247 | Vardesc *vd = getlocalvardesc(fs, i); |
250 | if (!vdinstack(vd)) | 248 | if (vd->vd.kind == RDKCTC) |
251 | return NULL; /* no debug info. for constants */ | 249 | return NULL; /* no debug info. for constants */ |
252 | else { | 250 | else { |
253 | int idx = vd->pidx; | 251 | int idx = vd->vd.pidx; |
254 | lua_assert(idx < fs->ndebugvars); | 252 | lua_assert(idx < fs->ndebugvars); |
255 | return &fs->f->locvars[idx]; | 253 | return &fs->f->locvars[idx]; |
256 | } | 254 | } |
@@ -261,7 +259,7 @@ static void init_var (FuncState *fs, expdesc *e, int i) { | |||
261 | e->f = e->t = NO_JUMP; | 259 | e->f = e->t = NO_JUMP; |
262 | e->k = VLOCAL; | 260 | e->k = VLOCAL; |
263 | e->u.var.vidx = i; | 261 | e->u.var.vidx = i; |
264 | e->u.var.sidx = getlocalvardesc(fs, i)->sidx; | 262 | e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx; |
265 | } | 263 | } |
266 | 264 | ||
267 | 265 | ||
@@ -269,15 +267,19 @@ static void check_readonly (LexState *ls, expdesc *e) { | |||
269 | FuncState *fs = ls->fs; | 267 | FuncState *fs = ls->fs; |
270 | TString *varname = NULL; /* to be set if variable is const */ | 268 | TString *varname = NULL; /* to be set if variable is const */ |
271 | switch (e->k) { | 269 | switch (e->k) { |
270 | case VCONST: { | ||
271 | varname = ls->dyd->actvar.arr[e->u.info].vd.name; | ||
272 | break; | ||
273 | } | ||
272 | case VLOCAL: { | 274 | case VLOCAL: { |
273 | Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); | 275 | Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx); |
274 | if (vardesc->ro) | 276 | if (vardesc->vd.kind != VDKREG) /* not a regular variable? */ |
275 | varname = vardesc->name; | 277 | varname = vardesc->vd.name; |
276 | break; | 278 | break; |
277 | } | 279 | } |
278 | case VUPVAL: { | 280 | case VUPVAL: { |
279 | Upvaldesc *up = &fs->f->upvalues[e->u.info]; | 281 | Upvaldesc *up = &fs->f->upvalues[e->u.info]; |
280 | if (up->ro) | 282 | if (up->kind != VDKREG) |
281 | varname = up->name; | 283 | varname = up->name; |
282 | break; | 284 | break; |
283 | } | 285 | } |
@@ -302,8 +304,8 @@ static void adjustlocalvars (LexState *ls, int nvars) { | |||
302 | for (i = 0; i < nvars; i++) { | 304 | for (i = 0; i < nvars; i++) { |
303 | int varidx = fs->nactvar++; | 305 | int varidx = fs->nactvar++; |
304 | Vardesc *var = getlocalvardesc(fs, varidx); | 306 | Vardesc *var = getlocalvardesc(fs, varidx); |
305 | var->sidx = stklevel++; | 307 | var->vd.sidx = stklevel++; |
306 | fs->f->locvars[var->pidx].startpc = fs->pc; | 308 | var->vd.pidx = registerlocalvar(ls, fs, var->vd.name); |
307 | } | 309 | } |
308 | } | 310 | } |
309 | 311 | ||
@@ -354,13 +356,13 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | |||
354 | if (v->k == VLOCAL) { | 356 | if (v->k == VLOCAL) { |
355 | up->instack = 1; | 357 | up->instack = 1; |
356 | up->idx = v->u.var.sidx; | 358 | up->idx = v->u.var.sidx; |
357 | up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro; | 359 | up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind; |
358 | lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name)); | 360 | lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name)); |
359 | } | 361 | } |
360 | else { | 362 | else { |
361 | up->instack = 0; | 363 | up->instack = 0; |
362 | up->idx = cast_byte(v->u.info); | 364 | up->idx = cast_byte(v->u.info); |
363 | up->ro = prev->f->upvalues[v->u.info].ro; | 365 | up->kind = prev->f->upvalues[v->u.info].kind; |
364 | lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); | 366 | lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name)); |
365 | } | 367 | } |
366 | up->name = name; | 368 | up->name = name; |
@@ -373,11 +375,17 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) { | |||
373 | ** Look for an active local variable with the name 'n' in the | 375 | ** Look for an active local variable with the name 'n' in the |
374 | ** function 'fs'. | 376 | ** function 'fs'. |
375 | */ | 377 | */ |
376 | static int searchvar (FuncState *fs, TString *n) { | 378 | static int searchvar (FuncState *fs, TString *n, expdesc *var) { |
377 | int i; | 379 | int i; |
378 | for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { | 380 | for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) { |
379 | if (eqstr(n, getlocalvardesc(fs, i)->name)) | 381 | Vardesc *vd = getlocalvardesc(fs, i); |
380 | return i; | 382 | if (eqstr(n, vd->vd.name)) { /* found? */ |
383 | if (vd->vd.kind == RDKCTC) /* compile-time constant? */ | ||
384 | init_exp(var, VCONST, fs->firstlocal + i); | ||
385 | else /* real variable */ | ||
386 | init_var(fs, var, i); | ||
387 | return var->k; | ||
388 | } | ||
381 | } | 389 | } |
382 | return -1; /* not found */ | 390 | return -1; /* not found */ |
383 | } | 391 | } |
@@ -405,20 +413,19 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { | |||
405 | if (fs == NULL) /* no more levels? */ | 413 | if (fs == NULL) /* no more levels? */ |
406 | init_exp(var, VVOID, 0); /* default is global */ | 414 | init_exp(var, VVOID, 0); /* default is global */ |
407 | else { | 415 | else { |
408 | int v = searchvar(fs, n); /* look up locals at current level */ | 416 | int v = searchvar(fs, n, var); /* look up locals at current level */ |
409 | if (v >= 0) { /* found? */ | 417 | if (v >= 0) { /* found? */ |
410 | init_var(fs, var, v); /* variable is local */ | 418 | if (v == VLOCAL && !base) |
411 | if (!base) | ||
412 | markupval(fs, var->u.var.vidx); /* local will be used as an upval */ | 419 | markupval(fs, var->u.var.vidx); /* local will be used as an upval */ |
413 | } | 420 | } |
414 | else { /* not found as local at current level; try upvalues */ | 421 | else { /* not found as local at current level; try upvalues */ |
415 | int idx = searchupvalue(fs, n); /* try existing upvalues */ | 422 | int idx = searchupvalue(fs, n); /* try existing upvalues */ |
416 | if (idx < 0) { /* not found? */ | 423 | if (idx < 0) { /* not found? */ |
417 | singlevaraux(fs->prev, n, var, 0); /* try upper levels */ | 424 | singlevaraux(fs->prev, n, var, 0); /* try upper levels */ |
418 | if (var->k == VVOID) /* not found? */ | 425 | if (var->k == VLOCAL || var->k == VUPVAL) /* local or upvalue? */ |
419 | return; /* it is a global */ | 426 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ |
420 | /* else was LOCAL or UPVAL */ | 427 | else /* it is a global or a constant */ |
421 | idx = newupvalue(fs, n, var); /* will be a new upvalue */ | 428 | return; /* don't need to do anything at this level */ |
422 | } | 429 | } |
423 | init_exp(var, VUPVAL, idx); /* new or old upvalue */ | 430 | init_exp(var, VUPVAL, idx); /* new or old upvalue */ |
424 | } | 431 | } |
@@ -483,7 +490,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { | |||
483 | ** local variable. | 490 | ** local variable. |
484 | */ | 491 | */ |
485 | static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { | 492 | static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) { |
486 | const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->name); | 493 | const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name); |
487 | const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; | 494 | const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'"; |
488 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); | 495 | msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname); |
489 | luaK_semerror(ls, msg); /* raise the error */ | 496 | luaK_semerror(ls, msg); /* raise the error */ |
@@ -1710,21 +1717,20 @@ static int getlocalattribute (LexState *ls) { | |||
1710 | const char *attr = getstr(str_checkname(ls)); | 1717 | const char *attr = getstr(str_checkname(ls)); |
1711 | checknext(ls, '>'); | 1718 | checknext(ls, '>'); |
1712 | if (strcmp(attr, "const") == 0) | 1719 | if (strcmp(attr, "const") == 0) |
1713 | return 1; /* read-only variable */ | 1720 | return RDKCONST; /* read-only variable */ |
1714 | else if (strcmp(attr, "toclose") == 0) | 1721 | else if (strcmp(attr, "toclose") == 0) |
1715 | return 2; /* to-be-closed variable */ | 1722 | return RDKTOCLOSE; /* to-be-closed variable */ |
1716 | else | 1723 | else |
1717 | luaK_semerror(ls, | 1724 | luaK_semerror(ls, |
1718 | luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); | 1725 | luaO_pushfstring(ls->L, "unknown attribute '%s'", attr)); |
1719 | } | 1726 | } |
1720 | return 0; | 1727 | return VDKREG; |
1721 | } | 1728 | } |
1722 | 1729 | ||
1723 | 1730 | ||
1724 | static void checktoclose (LexState *ls, int toclose) { | 1731 | static void checktoclose (LexState *ls, int level) { |
1725 | if (toclose != -1) { /* is there a to-be-closed variable? */ | 1732 | if (level != -1) { /* is there a to-be-closed variable? */ |
1726 | FuncState *fs = ls->fs; | 1733 | FuncState *fs = ls->fs; |
1727 | int level = luaY_nvarstack(fs) + toclose; | ||
1728 | markupval(fs, level + 1); | 1734 | markupval(fs, level + 1); |
1729 | fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ | 1735 | fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ |
1730 | luaK_codeABC(fs, OP_TBC, level, 0, 0); | 1736 | luaK_codeABC(fs, OP_TBC, level, 0, 0); |
@@ -1734,20 +1740,20 @@ static void checktoclose (LexState *ls, int toclose) { | |||
1734 | 1740 | ||
1735 | static void localstat (LexState *ls) { | 1741 | static void localstat (LexState *ls) { |
1736 | /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ | 1742 | /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */ |
1743 | FuncState *fs = ls->fs; | ||
1737 | int toclose = -1; /* index of to-be-closed variable (if any) */ | 1744 | int toclose = -1; /* index of to-be-closed variable (if any) */ |
1745 | Vardesc *var; /* last variable */ | ||
1738 | int nvars = 0; | 1746 | int nvars = 0; |
1739 | int nexps; | 1747 | int nexps; |
1740 | expdesc e; | 1748 | expdesc e; |
1741 | do { | 1749 | do { |
1742 | int kind = getlocalattribute(ls); | 1750 | int kind = getlocalattribute(ls); |
1743 | Vardesc *var = new_localvar(ls, str_checkname(ls)); | 1751 | var = new_localvar(ls, str_checkname(ls)); |
1744 | if (kind != 0) { /* is there an attribute? */ | 1752 | var->vd.kind = kind; |
1745 | var->ro = 1; /* all attributes make variable read-only */ | 1753 | if (kind == RDKTOCLOSE) { /* to-be-closed? */ |
1746 | if (kind == 2) { /* to-be-closed? */ | 1754 | if (toclose != -1) /* one already present? */ |
1747 | if (toclose != -1) /* one already present? */ | 1755 | luaK_semerror(ls, "multiple to-be-closed variables in local list"); |
1748 | luaK_semerror(ls, "multiple to-be-closed variables in local list"); | 1756 | toclose = luaY_nvarstack(fs) + nvars; |
1749 | toclose = nvars; | ||
1750 | } | ||
1751 | } | 1757 | } |
1752 | nvars++; | 1758 | nvars++; |
1753 | } while (testnext(ls, ',')); | 1759 | } while (testnext(ls, ',')); |
@@ -1757,9 +1763,18 @@ static void localstat (LexState *ls) { | |||
1757 | e.k = VVOID; | 1763 | e.k = VVOID; |
1758 | nexps = 0; | 1764 | nexps = 0; |
1759 | } | 1765 | } |
1760 | adjust_assign(ls, nvars, nexps, &e); | 1766 | if (nvars == nexps && /* no adjustments? */ |
1767 | var->vd.kind == RDKCONST && /* last variable is const? */ | ||
1768 | luaK_exp2const(fs, &e, &var->k)) { /* compile-time constant? */ | ||
1769 | var->vd.kind = RDKCTC; /* variable is a compile-time constant */ | ||
1770 | adjustlocalvars(ls, nvars - 1); /* exclude last variable */ | ||
1771 | fs->nactvar++; /* but count it */ | ||
1772 | } | ||
1773 | else { | ||
1774 | adjust_assign(ls, nvars, nexps, &e); | ||
1775 | adjustlocalvars(ls, nvars); | ||
1776 | } | ||
1761 | checktoclose(ls, toclose); | 1777 | checktoclose(ls, toclose); |
1762 | adjustlocalvars(ls, nvars); | ||
1763 | } | 1778 | } |
1764 | 1779 | ||
1765 | 1780 | ||
@@ -1925,7 +1940,7 @@ static void mainfunc (LexState *ls, FuncState *fs) { | |||
1925 | env = allocupvalue(fs); /* ...set environment upvalue */ | 1940 | env = allocupvalue(fs); /* ...set environment upvalue */ |
1926 | env->instack = 1; | 1941 | env->instack = 1; |
1927 | env->idx = 0; | 1942 | env->idx = 0; |
1928 | env->ro = 0; | 1943 | env->kind = VDKREG; |
1929 | env->name = ls->envn; | 1944 | env->name = ls->envn; |
1930 | luaX_next(ls); /* read first token */ | 1945 | luaX_next(ls); /* read first token */ |
1931 | statlist(ls); /* parse main body */ | 1946 | statlist(ls); /* parse main body */ |
@@ -34,8 +34,9 @@ typedef enum { | |||
34 | VNONRELOC, /* expression has its value in a fixed register; | 34 | VNONRELOC, /* expression has its value in a fixed register; |
35 | info = result register */ | 35 | info = result register */ |
36 | VLOCAL, /* local variable; var.ridx = local register; | 36 | VLOCAL, /* local variable; var.ridx = local register; |
37 | var.vidx = index in 'actvar.arr' */ | 37 | var.vidx = relative index in 'actvar.arr' */ |
38 | VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ | 38 | VUPVAL, /* upvalue variable; info = index of upvalue in 'upvalues' */ |
39 | VCONST, /* compile-time constant; info = absolute index in 'actvar.arr' */ | ||
39 | VINDEXED, /* indexed variable; | 40 | VINDEXED, /* indexed variable; |
40 | ind.t = table register; | 41 | ind.t = table register; |
41 | ind.idx = key's R index */ | 42 | ind.idx = key's R index */ |
@@ -81,19 +82,25 @@ typedef struct expdesc { | |||
81 | } expdesc; | 82 | } expdesc; |
82 | 83 | ||
83 | 84 | ||
85 | /* kinds of variables */ | ||
86 | #define VDKREG 0 /* regular */ | ||
87 | #define RDKCONST 1 /* constant */ | ||
88 | #define RDKTOCLOSE 2 /* to-be-closed */ | ||
89 | #define RDKCTC 3 /* compile-time constant */ | ||
90 | |||
84 | /* description of an active local variable */ | 91 | /* description of an active local variable */ |
85 | typedef struct Vardesc { | 92 | typedef union Vardesc { |
86 | TValuefields; /* constant value (if it is a compile-time constant) */ | 93 | struct { |
87 | lu_byte ro; /* true if variable is 'const' */ | 94 | TValuefields; /* constant value (if it is a compile-time constant) */ |
88 | lu_byte sidx; /* index of the variable in the stack */ | 95 | lu_byte kind; |
89 | short pidx; /* index of the variable in the Proto's 'locvars' array */ | 96 | lu_byte sidx; /* index of the variable in the stack */ |
90 | TString *name; /* variable name */ | 97 | short pidx; /* index of the variable in the Proto's 'locvars' array */ |
98 | TString *name; /* variable name */ | ||
99 | } vd; | ||
100 | TValue k; /* constant value (if any) */ | ||
91 | } Vardesc; | 101 | } Vardesc; |
92 | 102 | ||
93 | 103 | ||
94 | /* check whether Vardesc is in the stack (not a compile-time constant) */ | ||
95 | #define vdinstack(vd) (ttisnil(vd)) | ||
96 | |||
97 | 104 | ||
98 | /* description of pending goto statements and label statements */ | 105 | /* description of pending goto statements and label statements */ |
99 | typedef struct Labeldesc { | 106 | typedef struct Labeldesc { |
@@ -198,12 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) { | |||
198 | n = LoadInt(S); | 198 | n = LoadInt(S); |
199 | f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); | 199 | f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc); |
200 | f->sizeupvalues = n; | 200 | f->sizeupvalues = n; |
201 | for (i = 0; i < n; i++) | ||
202 | f->upvalues[i].name = NULL; | ||
203 | for (i = 0; i < n; i++) { | 201 | for (i = 0; i < n; i++) { |
202 | f->upvalues[i].name = NULL; | ||
204 | f->upvalues[i].instack = LoadByte(S); | 203 | f->upvalues[i].instack = LoadByte(S); |
205 | f->upvalues[i].idx = LoadByte(S); | 204 | f->upvalues[i].idx = LoadByte(S); |
206 | f->upvalues[i].ro = LoadByte(S); | 205 | f->upvalues[i].kind = LoadByte(S); |
207 | } | 206 | } |
208 | } | 207 | } |
209 | 208 | ||
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. | |||
223 | so changing its value will affect only your own code.) | 223 | so changing its value will affect only your own code.) |
224 | 224 | ||
225 | When Lua loads a chunk, | 225 | When Lua loads a chunk, |
226 | the default value for its @id{_ENV} upvalue | 226 | the default value for its @id{_ENV} variable |
227 | is the global environment @seeF{load}. | 227 | is the global environment @seeF{load}. |
228 | Therefore, by default, | 228 | Therefore, by default, |
229 | free names in Lua code refer to entries in the global environment | 229 | free names in Lua code refer to entries in the global environment |
@@ -233,7 +233,7 @@ and some functions there operate on that environment. | |||
233 | You can use @Lid{load} (or @Lid{loadfile}) | 233 | You can use @Lid{load} (or @Lid{loadfile}) |
234 | to load a chunk with a different environment. | 234 | to load a chunk with a different environment. |
235 | (In C, you have to load the chunk and then change the value | 235 | (In C, you have to load the chunk and then change the value |
236 | of its first upvalue.) | 236 | of its first upvalue; see @See{lua_setupvalue}.) |
237 | 237 | ||
238 | } | 238 | } |
239 | 239 | ||
@@ -1224,7 +1224,7 @@ As such, chunks can define local variables, | |||
1224 | receive arguments, and return values. | 1224 | receive arguments, and return values. |
1225 | Moreover, such anonymous function is compiled as in the | 1225 | Moreover, such anonymous function is compiled as in the |
1226 | scope of an external local variable called @id{_ENV} @see{globalenv}. | 1226 | scope of an external local variable called @id{_ENV} @see{globalenv}. |
1227 | The resulting function always has @id{_ENV} as its only upvalue, | 1227 | The resulting function always has @id{_ENV} as its only external variable, |
1228 | even if it does not use that variable. | 1228 | even if it does not use that variable. |
1229 | 1229 | ||
1230 | A chunk can be stored in a file or in a string inside the host program. | 1230 | 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. | |||
2241 | Because of the @x{lexical scoping} rules, | 2241 | Because of the @x{lexical scoping} rules, |
2242 | local variables can be freely accessed by functions | 2242 | local variables can be freely accessed by functions |
2243 | defined inside their scope. | 2243 | defined inside their scope. |
2244 | A local variable used by an inner function is called | 2244 | A local variable used by an inner function is called an @def{upvalue} |
2245 | an @def{upvalue}, or @emphx{external local variable}, | 2245 | (or @emphx{external local variable}, or simply @emphx{external variable}) |
2246 | inside the inner function. | 2246 | inside the inner function. |
2247 | 2247 | ||
2248 | Notice that each execution of a @Rw{local} statement | 2248 | Notice that each execution of a @Rw{local} statement |
@@ -4765,11 +4765,7 @@ and returns its name. | |||
4765 | Returns @id{NULL} (and pushes nothing) | 4765 | Returns @id{NULL} (and pushes nothing) |
4766 | when the index @id{n} is greater than the number of upvalues. | 4766 | when the index @id{n} is greater than the number of upvalues. |
4767 | 4767 | ||
4768 | For @N{C functions}, this function uses the empty string @T{""} | 4768 | See @Lid{debug.getupvalue} for more information about upvalues. |
4769 | as a name for all upvalues. | ||
4770 | (For Lua functions, | ||
4771 | upvalues are the external local variables that the function uses, | ||
4772 | and that are consequently included in its closure.) | ||
4773 | 4769 | ||
4774 | } | 4770 | } |
4775 | 4771 | ||
@@ -8485,6 +8481,8 @@ The first parameter or local variable has @N{index 1}, and so on, | |||
8485 | following the order that they are declared in the code, | 8481 | following the order that they are declared in the code, |
8486 | counting only the variables that are active | 8482 | counting only the variables that are active |
8487 | in the current scope of the function. | 8483 | in the current scope of the function. |
8484 | Compile-time constants may not appear in this listing, | ||
8485 | if they were optimized away by the compiler. | ||
8488 | Negative indices refer to vararg arguments; | 8486 | Negative indices refer to vararg arguments; |
8489 | @num{-1} is the first vararg argument. | 8487 | @num{-1} is the first vararg argument. |
8490 | The function returns @nil if there is no variable with the given index, | 8488 | 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 | |||
8520 | with index @id{up} of the function @id{f}. | 8518 | with index @id{up} of the function @id{f}. |
8521 | The function returns @nil if there is no upvalue with the given index. | 8519 | The function returns @nil if there is no upvalue with the given index. |
8522 | 8520 | ||
8523 | Variable names starting with @Char{(} (open parenthesis) @C{)} | 8521 | (For Lua functions, |
8524 | represent variables with no known names | 8522 | upvalues are the external local variables that the function uses, |
8523 | and that are consequently included in its closure.) | ||
8524 | |||
8525 | For @N{C functions}, this function uses the empty string @T{""} | ||
8526 | as a name for all upvalues. | ||
8527 | |||
8528 | Variable name @Char{?} (interrogation mark) | ||
8529 | represents variables with no known names | ||
8525 | (variables from chunks saved without debug information). | 8530 | (variables from chunks saved without debug information). |
8526 | 8531 | ||
8527 | } | 8532 | } |
@@ -8626,6 +8631,8 @@ The function returns @nil if there is no upvalue | |||
8626 | with the given index. | 8631 | with the given index. |
8627 | Otherwise, it returns the name of the upvalue. | 8632 | Otherwise, it returns the name of the upvalue. |
8628 | 8633 | ||
8634 | See @Lid{debug.getupvalue} for more information about upvalues. | ||
8635 | |||
8629 | } | 8636 | } |
8630 | 8637 | ||
8631 | @LibEntry{debug.setuservalue (udata, value, n)| | 8638 | @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 | |||
7 | end | 7 | end |
8 | print "testing code generation and optimizations" | 8 | print "testing code generation and optimizations" |
9 | 9 | ||
10 | -- to test constant propagation | ||
11 | local <const> k0 = 0 | ||
12 | local <const> k1 = 1 | ||
13 | local <const> k3 = 3 | ||
14 | local <const> k6 = k3 + (k3 << k0) | ||
15 | local <const> kFF0 = 0xFF0 | ||
16 | local <const> k3_78 = 3.78 | ||
17 | local <const> x, <const> k3_78_4 = 10, k3_78 / 4 | ||
18 | assert(x == 10) | ||
19 | |||
20 | local <const> kx = "x" | ||
21 | |||
22 | local <const> kTrue = true | ||
23 | local <const> kFalse = false | ||
24 | |||
25 | local <const> kNil = nil | ||
10 | 26 | ||
11 | -- this code gave an error for the code checker | 27 | -- this code gave an error for the code checker |
12 | do | 28 | do |
@@ -27,12 +43,12 @@ end | |||
27 | 43 | ||
28 | local function foo () | 44 | local function foo () |
29 | local a | 45 | local a |
30 | a = 3; | 46 | a = k3; |
31 | a = 0; a = 0.0; a = -7 + 7 | 47 | a = 0; a = 0.0; a = -7 + 7 |
32 | a = 3.78/4; a = 3.78/4 | 48 | a = k3_78/4; a = k3_78_4 |
33 | a = -3.78/4; a = 3.78/4; a = -3.78/4 | 49 | a = -k3_78/4; a = k3_78/4; a = -3.78/4 |
34 | a = -3.79/4; a = 0.0; a = -0; | 50 | a = -3.79/4; a = 0.0; a = -0; |
35 | a = 3; a = 3.0; a = 3; a = 3.0 | 51 | a = k3; a = 3.0; a = 3; a = 3.0 |
36 | end | 52 | end |
37 | 53 | ||
38 | checkKlist(foo, {3.78/4, -3.78/4, -3.79/4}) | 54 | checkKlist(foo, {3.78/4, -3.78/4, -3.79/4}) |
@@ -86,10 +102,11 @@ end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN') | |||
86 | 102 | ||
87 | -- sequence of LOADNILs | 103 | -- sequence of LOADNILs |
88 | check(function () | 104 | check(function () |
105 | local <const> kNil = nil | ||
89 | local a,b,c | 106 | local a,b,c |
90 | local d; local e; | 107 | local d; local e; |
91 | local f,g,h; | 108 | local f,g,h; |
92 | d = nil; d=nil; b=nil; a=nil; c=nil; | 109 | d = nil; d=nil; b=nil; a=kNil; c=nil; |
93 | end, 'LOADNIL', 'RETURN0') | 110 | end, 'LOADNIL', 'RETURN0') |
94 | 111 | ||
95 | check(function () | 112 | check(function () |
@@ -109,7 +126,7 @@ check (function (a,b,c) return a end, 'RETURN1') | |||
109 | 126 | ||
110 | 127 | ||
111 | -- infinite loops | 128 | -- infinite loops |
112 | check(function () while true do local a = -1 end end, | 129 | check(function () while kTrue do local a = -1 end end, |
113 | 'LOADI', 'JMP', 'RETURN0') | 130 | 'LOADI', 'JMP', 'RETURN0') |
114 | 131 | ||
115 | check(function () while 1 do local a = -1 end end, | 132 | 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, | |||
125 | 142 | ||
126 | -- not | 143 | -- not |
127 | check(function () return not not nil end, 'LOADBOOL', 'RETURN1') | 144 | check(function () return not not nil end, 'LOADBOOL', 'RETURN1') |
128 | check(function () return not not false end, 'LOADBOOL', 'RETURN1') | 145 | check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1') |
129 | check(function () return not not true end, 'LOADBOOL', 'RETURN1') | 146 | check(function () return not not true end, 'LOADBOOL', 'RETURN1') |
130 | check(function () return not not 1 end, 'LOADBOOL', 'RETURN1') | 147 | check(function () return not not k3 end, 'LOADBOOL', 'RETURN1') |
131 | 148 | ||
132 | -- direct access to locals | 149 | -- direct access to locals |
133 | check(function () | 150 | check(function () |
@@ -144,7 +161,8 @@ end, | |||
144 | -- direct access to constants | 161 | -- direct access to constants |
145 | check(function () | 162 | check(function () |
146 | local a,b | 163 | local a,b |
147 | a.x = 3.2 | 164 | local c = kNil |
165 | a[kx] = 3.2 | ||
148 | a.x = b | 166 | a.x = b |
149 | a[b] = 'x' | 167 | a[b] = 'x' |
150 | end, | 168 | end, |
@@ -152,8 +170,9 @@ end, | |||
152 | 170 | ||
153 | -- "get/set table" with numeric indices | 171 | -- "get/set table" with numeric indices |
154 | check(function (a) | 172 | check(function (a) |
173 | local <const> k255 = 255 | ||
155 | a[1] = a[100] | 174 | a[1] = a[100] |
156 | a[255] = a[256] | 175 | a[k255] = a[256] |
157 | a[256] = 5 | 176 | a[256] = 5 |
158 | end, | 177 | end, |
159 | 'GETI', 'SETI', | 178 | 'GETI', 'SETI', |
@@ -170,7 +189,7 @@ end, | |||
170 | 189 | ||
171 | check(function () | 190 | check(function () |
172 | local a,b | 191 | local a,b |
173 | a[true] = false | 192 | a[kTrue] = false |
174 | end, | 193 | end, |
175 | 'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0') | 194 | 'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0') |
176 | 195 | ||
@@ -238,37 +257,39 @@ local function checkF (func, val) | |||
238 | end | 257 | end |
239 | 258 | ||
240 | checkF(function () return 0.0 end, 0.0) | 259 | checkF(function () return 0.0 end, 0.0) |
241 | checkI(function () return 0 end, 0) | 260 | checkI(function () return k0 end, 0) |
242 | checkI(function () return -0//1 end, 0) | 261 | checkI(function () return -k0//1 end, 0) |
243 | checkK(function () return 3^-1 end, 1/3) | 262 | checkK(function () return 3^-1 end, 1/3) |
244 | checkK(function () return (1 + 1)^(50 + 50) end, 2^100) | 263 | checkK(function () return (1 + 1)^(50 + 50) end, 2^100) |
245 | checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0) | 264 | checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0) |
246 | checkF(function () return (-3^0 + 5) // 3.0 end, 1.0) | 265 | checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0) |
247 | checkI(function () return -3 % 5 end, 2) | 266 | checkI(function () return -k3 % 5 end, 2) |
248 | checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0) | 267 | checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0) |
249 | checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0) | 268 | checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0) |
250 | checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4) | 269 | checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4) |
251 | checkI(function () return ~(~0xFF0 | 0xFF0) end, 0) | 270 | checkI(function () return ~(~kFF0 | kFF0) end, 0) |
252 | checkI(function () return ~~-1024.0 end, -1024) | 271 | checkI(function () return ~~-1024.0 end, -1024) |
253 | checkI(function () return ((100 << 6) << -4) >> 2 end, 100) | 272 | checkI(function () return ((100 << k6) << -4) >> 2 end, 100) |
254 | 273 | ||
255 | -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535) | 274 | -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535) |
256 | local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding | 275 | local a = 17; local sbx = ((1 << a) - 1) >> 1 -- avoid folding |
257 | checkI(function () return 65535 end, sbx) | 276 | local <const> border = 65535 |
258 | checkI(function () return -65535 end, -sbx) | 277 | checkI(function () return border end, sbx) |
259 | checkI(function () return 65536 end, sbx + 1) | 278 | checkI(function () return -border end, -sbx) |
260 | checkK(function () return 65537 end, sbx + 2) | 279 | checkI(function () return border + 1 end, sbx + 1) |
261 | checkK(function () return -65536 end, -(sbx + 1)) | 280 | checkK(function () return border + 2 end, sbx + 2) |
281 | checkK(function () return -(border + 1) end, -(sbx + 1)) | ||
262 | 282 | ||
263 | checkF(function () return 65535.0 end, sbx + 0.0) | 283 | local <const> border = 65535.0 |
264 | checkF(function () return -65535.0 end, -sbx + 0.0) | 284 | checkF(function () return border end, sbx + 0.0) |
265 | checkF(function () return 65536.0 end, (sbx + 1.0)) | 285 | checkF(function () return -border end, -sbx + 0.0) |
266 | checkK(function () return 65537.0 end, (sbx + 2.0)) | 286 | checkF(function () return border + 1 end, (sbx + 1.0)) |
267 | checkK(function () return -65536.0 end, -(sbx + 1.0)) | 287 | checkK(function () return border + 2 end, (sbx + 2.0)) |
288 | checkK(function () return -(border + 1) end, -(sbx + 1.0)) | ||
268 | 289 | ||
269 | 290 | ||
270 | -- immediate operands | 291 | -- immediate operands |
271 | checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1') | 292 | checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'RETURN1') |
272 | checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1') | 293 | checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1') |
273 | checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1') | 294 | checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1') |
274 | checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1') | 295 | 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') | |||
276 | checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1') | 297 | checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1') |
277 | checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1') | 298 | checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1') |
278 | checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1') | 299 | checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1') |
279 | checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1') | 300 | checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'RETURN1') |
280 | checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1') | 301 | checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1') |
281 | checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1') | 302 | checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1') |
282 | checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1') | 303 | 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') | |||
295 | 316 | ||
296 | -- no foldings (and immediate operands) | 317 | -- no foldings (and immediate operands) |
297 | check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1') | 318 | check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1') |
298 | check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1') | 319 | check(function () return k3/0 end, 'LOADI', 'DIVI', 'RETURN1') |
299 | check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1') | 320 | check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1') |
300 | check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1') | 321 | check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1') |
301 | check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1') | 322 | check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1') |
@@ -335,7 +356,7 @@ end, | |||
335 | 356 | ||
336 | do -- tests for table access in upvalues | 357 | do -- tests for table access in upvalues |
337 | local t | 358 | local t |
338 | check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP') | 359 | check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP') |
339 | check(function (a) t[a()] = t[a()] end, | 360 | check(function (a) t[a()] = t[a()] end, |
340 | 'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL', | 361 | 'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL', |
341 | 'GETUPVAL', 'GETTABLE', 'SETTABLE') | 362 | 'GETUPVAL', 'GETTABLE', 'SETTABLE') |
@@ -379,6 +400,12 @@ function (a) | |||
379 | end | 400 | end |
380 | ) | 401 | ) |
381 | 402 | ||
403 | checkequal(function () return 6 or true or nil end, | ||
404 | function () return k6 or kTrue or kNil end) | ||
405 | |||
406 | checkequal(function () return 6 and true or nil end, | ||
407 | function () return k6 and kTrue or kNil end) | ||
408 | |||
382 | 409 | ||
383 | print 'OK' | 410 | print 'OK' |
384 | 411 | ||
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) | |||
287 | ------------------------------------------------------------------ | 287 | ------------------------------------------------------------------ |
288 | 288 | ||
289 | -- sometimes will be 0, sometimes will not... | 289 | -- sometimes will be 0, sometimes will not... |
290 | _ENV.GLOB1 = math.floor(os.time()) % 2 | 290 | _ENV.GLOB1 = math.random(0, 1) |
291 | 291 | ||
292 | -- basic expressions with their respective values | 292 | -- basic expressions with their respective values |
293 | local basiccases = { | 293 | local basiccases = { |
@@ -298,6 +298,26 @@ local basiccases = { | |||
298 | {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1}, | 298 | {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1}, |
299 | } | 299 | } |
300 | 300 | ||
301 | local prog | ||
302 | |||
303 | if _ENV.GLOB1 == 0 then | ||
304 | basiccases[2][1] = "F" -- constant false | ||
305 | |||
306 | prog = [[ | ||
307 | local <const> F = false | ||
308 | if %s then IX = true end | ||
309 | return %s | ||
310 | ]] | ||
311 | else | ||
312 | basiccases[4][1] = "k10" -- constant 10 | ||
313 | |||
314 | prog = [[ | ||
315 | local <const> k10 = 10 | ||
316 | if %s then IX = true end | ||
317 | return %s | ||
318 | ]] | ||
319 | end | ||
320 | |||
301 | print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')') | 321 | print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')') |
302 | 322 | ||
303 | 323 | ||
@@ -337,8 +357,6 @@ cases[1] = basiccases | |||
337 | for i = 2, level do cases[i] = createcases(i) end | 357 | for i = 2, level do cases[i] = createcases(i) end |
338 | print("+") | 358 | print("+") |
339 | 359 | ||
340 | local prog = [[if %s then IX = true end; return %s]] | ||
341 | |||
342 | local i = 0 | 360 | local i = 0 |
343 | for n = 1, level do | 361 | for n = 1, level do |
344 | for _, v in pairs(cases[n]) do | 362 | 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 | |||
324 | 324 | ||
325 | -- errors due to non-closable values | 325 | -- errors due to non-closable values |
326 | local function foo () | 326 | local function foo () |
327 | local <toclose> x = 34 | 327 | local <toclose> x = {} |
328 | end | 328 | end |
329 | local stat, msg = pcall(foo) | 329 | local stat, msg = pcall(foo) |
330 | assert(not stat and string.find(msg, "variable 'x'")) | 330 | 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 | |||
270 | end | 270 | end |
271 | 271 | ||
272 | do | 272 | do |
273 | local NaN = 0/0 | 273 | local <const> NaN = 0/0 |
274 | assert(not (NaN < 0)) | 274 | assert(not (NaN < 0)) |
275 | assert(not (NaN > minint)) | 275 | assert(not (NaN > minint)) |
276 | assert(not (NaN <= -9)) | 276 | assert(not (NaN <= -9)) |
@@ -767,7 +767,8 @@ assert(a == '10' and b == '20') | |||
767 | 767 | ||
768 | do | 768 | do |
769 | print("testing -0 and NaN") | 769 | print("testing -0 and NaN") |
770 | local mz, z = -0.0, 0.0 | 770 | local <const> mz = -0.0 |
771 | local <const> z = 0.0 | ||
771 | assert(mz == z) | 772 | assert(mz == z) |
772 | assert(1/mz < 0 and 0 < 1/z) | 773 | assert(1/mz < 0 and 0 < 1/z) |
773 | local a = {[mz] = 1} | 774 | local a = {[mz] = 1} |
@@ -775,17 +776,18 @@ do | |||
775 | a[z] = 2 | 776 | a[z] = 2 |
776 | assert(a[z] == 2 and a[mz] == 2) | 777 | assert(a[z] == 2 and a[mz] == 2) |
777 | local inf = math.huge * 2 + 1 | 778 | local inf = math.huge * 2 + 1 |
778 | mz, z = -1/inf, 1/inf | 779 | local <const> mz = -1/inf |
780 | local <const> z = 1/inf | ||
779 | assert(mz == z) | 781 | assert(mz == z) |
780 | assert(1/mz < 0 and 0 < 1/z) | 782 | assert(1/mz < 0 and 0 < 1/z) |
781 | local NaN = inf - inf | 783 | local <const> NaN = inf - inf |
782 | assert(NaN ~= NaN) | 784 | assert(NaN ~= NaN) |
783 | assert(not (NaN < NaN)) | 785 | assert(not (NaN < NaN)) |
784 | assert(not (NaN <= NaN)) | 786 | assert(not (NaN <= NaN)) |
785 | assert(not (NaN > NaN)) | 787 | assert(not (NaN > NaN)) |
786 | assert(not (NaN >= NaN)) | 788 | assert(not (NaN >= NaN)) |
787 | assert(not (0 < NaN) and not (NaN < 0)) | 789 | assert(not (0 < NaN) and not (NaN < 0)) |
788 | local NaN1 = 0/0 | 790 | local <const> NaN1 = 0/0 |
789 | assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN)) | 791 | assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN)) |
790 | local a = {} | 792 | local a = {} |
791 | assert(not pcall(rawset, a, NaN, 1)) | 793 | assert(not pcall(rawset, a, NaN, 1)) |
@@ -814,8 +816,8 @@ end | |||
814 | -- the first call after seed 1007 should return 0x7a7040a5a323c9d6 | 816 | -- the first call after seed 1007 should return 0x7a7040a5a323c9d6 |
815 | do | 817 | do |
816 | -- all computations assume at most 32-bit integers | 818 | -- all computations assume at most 32-bit integers |
817 | local h = 0x7a7040a5 -- higher half | 819 | local <const> h = 0x7a7040a5 -- higher half |
818 | local l = 0xa323c9d6 -- lower half | 820 | local <const> l = 0xa323c9d6 -- lower half |
819 | 821 | ||
820 | math.randomseed(1007) | 822 | math.randomseed(1007) |
821 | -- get the low 'intbits' of the 64-bit expected result | 823 | -- get the low 'intbits' of the 64-bit expected result |