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 |
