Browse Source

First implementation of constant propagation

Local constant variables initialized with compile-time constants
are optimized away from the code.
Roberto Ierusalimschy 6 years ago
parent
commit
f6aab3ec1f
12 changed files with 249 additions and 119 deletions
  1. 55 1
      lcode.c
  2. 1 0
      lcode.h
  3. 1 1
      ldump.c
  4. 1 1
      lobject.h
  5. 64 49
      lparser.c
  6. 17 10
      lparser.h
  7. 2 3
      lundump.c
  8. 19 12
      manual/manual.of
  9. 58 31
      testes/code.lua
  10. 21 3
      testes/constructs.lua
  11. 1 1
      testes/locals.lua
  12. 9 7
      testes/math.lua

+ 55 - 1
lcode.c

@@ -67,6 +67,30 @@ static int tonumeral (const expdesc *e, TValue *v) {
 }
 }
 
 
 
 
+/*
+** If expression is a constant, fills 'v' with its value
+** and returns 1. Otherwise, returns 0.
+*/
+int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v) {
+  if (hasjumps(e))
+    return 0;  /* not a constant */
+  switch (e->k) {
+    case VFALSE: case VTRUE:
+      setbvalue(v, e->k == VTRUE);
+      return 1;
+    case VNIL:
+      setnilvalue(v);
+      return 1;
+    case VK: {
+      TValue *k = &fs->f->k[e->u.info];
+      setobj(fs->ls->L, v, k);
+      return 1;
+    }
+    default: return tonumeral(e, v);
+  }
+}
+
+
 /*
 /*
 ** Return the previous instruction of the current code. If there
 ** Return the previous instruction of the current code. If there
 ** may be a jump target between the current instruction and the
 ** may be a jump target between the current instruction and the
@@ -629,6 +653,31 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
 }
 }
 
 
 
 
+/*
+** Convert a constant in 'v' into an expression description 'e'
+*/
+static void const2exp (FuncState *fs, TValue *v, expdesc *e) {
+  switch (ttypetag(v)) {
+    case LUA_TNUMINT:
+      e->k = VKINT; e->u.ival = ivalue(v);
+      break;
+    case LUA_TNUMFLT:
+      e->k = VKFLT; e->u.nval = fltvalue(v);
+      break;
+    case LUA_TBOOLEAN:
+      e->k = bvalue(v) ? VTRUE : VFALSE;
+      break;
+    case LUA_TNIL:
+      e->k = VNIL;
+      break;
+    case LUA_TSHRSTR:  case LUA_TLNGSTR:
+      e->k = VK; e->u.info = luaK_stringK(fs, tsvalue(v));
+      break;
+    default: lua_assert(0);
+  }
+}
+
+
 /*
 /*
 ** Fix an expression to return the number of results 'nresults'.
 ** Fix an expression to return the number of results 'nresults'.
 ** Either 'e' is a multi-ret expression (function call or vararg)
 ** Either 'e' is a multi-ret expression (function call or vararg)
@@ -677,6 +726,11 @@ void luaK_setoneret (FuncState *fs, expdesc *e) {
 */
 */
 void luaK_dischargevars (FuncState *fs, expdesc *e) {
 void luaK_dischargevars (FuncState *fs, expdesc *e) {
   switch (e->k) {
   switch (e->k) {
+    case VCONST: {
+      TValue *val = &fs->ls->dyd->actvar.arr[e->u.info].k;
+      const2exp(fs, val, e);
+      break;
+    }
     case VLOCAL: {  /* already in a register */
     case VLOCAL: {  /* already in a register */
       e->u.info = e->u.var.sidx;
       e->u.info = e->u.var.sidx;
       e->k = VNONRELOC;  /* becomes a non-relocatable value */
       e->k = VNONRELOC;  /* becomes a non-relocatable value */
@@ -1074,7 +1128,6 @@ void luaK_goiffalse (FuncState *fs, expdesc *e) {
 ** Code 'not e', doing constant folding.
 ** Code 'not e', doing constant folding.
 */
 */
 static void codenot (FuncState *fs, expdesc *e) {
 static void codenot (FuncState *fs, expdesc *e) {
-  luaK_dischargevars(fs, e);
   switch (e->k) {
   switch (e->k) {
     case VNIL: case VFALSE: {
     case VNIL: case VFALSE: {
       e->k = VTRUE;  /* true == not nil == not false */
       e->k = VTRUE;  /* true == not nil == not false */
@@ -1447,6 +1500,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
 */
 */
 void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
 void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
   static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
   static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
+  luaK_dischargevars(fs, e);
   switch (op) {
   switch (op) {
     case OPR_MINUS: case OPR_BNOT:  /* use 'ef' as fake 2nd operand */
     case OPR_MINUS: case OPR_BNOT:  /* use 'ef' as fake 2nd operand */
       if (constfolding(fs, op + LUA_OPUNM, e, &ef))
       if (constfolding(fs, op + LUA_OPUNM, e, &ef))

+ 1 - 0
lcode.h

@@ -56,6 +56,7 @@ LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
 LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
 LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
                                             int B, int C, int k);
                                             int B, int C, int k);
 LUAI_FUNC int luaK_isKint (expdesc *e);
 LUAI_FUNC int luaK_isKint (expdesc *e);
+LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
 LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
 LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
 LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
 LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
 LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
 LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);

+ 1 - 1
ldump.c

@@ -149,7 +149,7 @@ static void DumpUpvalues (const Proto *f, DumpState *D) {
   for (i = 0; i < n; i++) {
   for (i = 0; i < n; i++) {
     DumpByte(f->upvalues[i].instack, D);
     DumpByte(f->upvalues[i].instack, D);
     DumpByte(f->upvalues[i].idx, D);
     DumpByte(f->upvalues[i].idx, D);
-    DumpByte(f->upvalues[i].ro, D);
+    DumpByte(f->upvalues[i].kind, D);
   }
   }
 }
 }
 
 

+ 1 - 1
lobject.h

@@ -460,7 +460,7 @@ typedef struct Upvaldesc {
   TString *name;  /* upvalue name (for debug information) */
   TString *name;  /* upvalue name (for debug information) */
   lu_byte instack;  /* whether it is in stack (register) */
   lu_byte instack;  /* whether it is in stack (register) */
   lu_byte idx;  /* index of upvalue (in stack or in outer function's list) */
   lu_byte idx;  /* index of upvalue (in stack or in outer function's list) */
-  lu_byte ro;  /* true if upvalue is read-only (const) */
+  lu_byte kind;  /* kind of corresponding variable */
 } Upvaldesc;
 } Upvaldesc;
 
 
 
 

+ 64 - 49
lparser.c

@@ -170,15 +170,16 @@ static void codename (LexState *ls, expdesc *e) {
 ** Register a new local variable in the active 'Proto' (for debug
 ** Register a new local variable in the active 'Proto' (for debug
 ** information).
 ** information).
 */
 */
-static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) {
+static int registerlocalvar (LexState *ls, FuncState *fs, TString *varname) {
   Proto *f = fs->f;
   Proto *f = fs->f;
   int oldsize = f->sizelocvars;
   int oldsize = f->sizelocvars;
-  luaM_growvector(L, f->locvars, fs->ndebugvars, f->sizelocvars,
+  luaM_growvector(ls->L, f->locvars, fs->ndebugvars, f->sizelocvars,
                   LocVar, SHRT_MAX, "local variables");
                   LocVar, SHRT_MAX, "local variables");
   while (oldsize < f->sizelocvars)
   while (oldsize < f->sizelocvars)
     f->locvars[oldsize++].varname = NULL;
     f->locvars[oldsize++].varname = NULL;
   f->locvars[fs->ndebugvars].varname = varname;
   f->locvars[fs->ndebugvars].varname = varname;
-  luaC_objbarrier(L, f, varname);
+  f->locvars[fs->ndebugvars].startpc = fs->pc;
+  luaC_objbarrier(ls->L, f, varname);
   return fs->ndebugvars++;
   return fs->ndebugvars++;
 }
 }
 
 
@@ -191,16 +192,13 @@ static Vardesc *new_localvar (LexState *ls, TString *name) {
   FuncState *fs = ls->fs;
   FuncState *fs = ls->fs;
   Dyndata *dyd = ls->dyd;
   Dyndata *dyd = ls->dyd;
   Vardesc *var;
   Vardesc *var;
-  int reg = registerlocalvar(L, fs, name);
   checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
   checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
                  MAXVARS, "local variables");
                  MAXVARS, "local variables");
   luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
   luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
                   dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
                   dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
   var = &dyd->actvar.arr[dyd->actvar.n++];
   var = &dyd->actvar.arr[dyd->actvar.n++];
-  var->pidx = cast(short, reg);
-  var->ro = 0;
-  var->name = name;
-  setnilvalue(var);
+  var->vd.kind = VDKREG;  /* default is a regular variable */
+  var->vd.name = name;
   return var;
   return var;
 }
 }
 
 
@@ -225,8 +223,8 @@ static Vardesc *getlocalvardesc (FuncState *fs, int i) {
 static int stacklevel (FuncState *fs, int nvar) {
 static int stacklevel (FuncState *fs, int nvar) {
   while (nvar > 0) {
   while (nvar > 0) {
     Vardesc *vd = getlocalvardesc(fs, nvar - 1);
     Vardesc *vd = getlocalvardesc(fs, nvar - 1);
-    if (vdinstack(vd))  /* is in the stack? */
-      return vd->sidx + 1;
+    if (vd->vd.kind != RDKCTC)  /* is in the stack? */
+      return vd->vd.sidx + 1;
     else
     else
       nvar--;  /* try previous variable */
       nvar--;  /* try previous variable */
   }
   }
@@ -247,10 +245,10 @@ int luaY_nvarstack (FuncState *fs) {
 */
 */
 static LocVar *localdebuginfo (FuncState *fs, int i) {
 static LocVar *localdebuginfo (FuncState *fs, int i) {
   Vardesc *vd = getlocalvardesc(fs, i);
   Vardesc *vd = getlocalvardesc(fs, i);
-  if (!vdinstack(vd))
+  if (vd->vd.kind == RDKCTC)
     return NULL;  /* no debug info. for constants */
     return NULL;  /* no debug info. for constants */
   else {
   else {
-    int idx = vd->pidx;
+    int idx = vd->vd.pidx;
     lua_assert(idx < fs->ndebugvars);
     lua_assert(idx < fs->ndebugvars);
     return &fs->f->locvars[idx];
     return &fs->f->locvars[idx];
   }
   }
@@ -261,7 +259,7 @@ static void init_var (FuncState *fs, expdesc *e, int i) {
   e->f = e->t = NO_JUMP;
   e->f = e->t = NO_JUMP;
   e->k = VLOCAL;
   e->k = VLOCAL;
   e->u.var.vidx = i;
   e->u.var.vidx = i;
-  e->u.var.sidx = getlocalvardesc(fs, i)->sidx;
+  e->u.var.sidx = getlocalvardesc(fs, i)->vd.sidx;
 }
 }
 
 
 
 
@@ -269,15 +267,19 @@ static void check_readonly (LexState *ls, expdesc *e) {
   FuncState *fs = ls->fs;
   FuncState *fs = ls->fs;
   TString *varname = NULL;  /* to be set if variable is const */
   TString *varname = NULL;  /* to be set if variable is const */
   switch (e->k) {
   switch (e->k) {
+    case VCONST: {
+      varname = ls->dyd->actvar.arr[e->u.info].vd.name;
+      break;
+    }
     case VLOCAL: {
     case VLOCAL: {
       Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
       Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
-      if (vardesc->ro)
-        varname = vardesc->name;
+      if (vardesc->vd.kind != VDKREG)  /* not a regular variable? */
+        varname = vardesc->vd.name;
       break;
       break;
     }
     }
     case VUPVAL: {
     case VUPVAL: {
       Upvaldesc *up = &fs->f->upvalues[e->u.info];
       Upvaldesc *up = &fs->f->upvalues[e->u.info];
-      if (up->ro)
+      if (up->kind != VDKREG)
         varname = up->name;
         varname = up->name;
       break;
       break;
     }
     }
@@ -302,8 +304,8 @@ static void adjustlocalvars (LexState *ls, int nvars) {
   for (i = 0; i < nvars; i++) {
   for (i = 0; i < nvars; i++) {
     int varidx = fs->nactvar++;
     int varidx = fs->nactvar++;
     Vardesc *var = getlocalvardesc(fs, varidx);
     Vardesc *var = getlocalvardesc(fs, varidx);
-    var->sidx = stklevel++;
-    fs->f->locvars[var->pidx].startpc = fs->pc;
+    var->vd.sidx = stklevel++;
+    var->vd.pidx = registerlocalvar(ls, fs, var->vd.name);
   }
   }
 }
 }
 
 
@@ -354,13 +356,13 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
   if (v->k == VLOCAL) {
   if (v->k == VLOCAL) {
     up->instack = 1;
     up->instack = 1;
     up->idx = v->u.var.sidx;
     up->idx = v->u.var.sidx;
-    up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro;
-    lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name));
+    up->kind = getlocalvardesc(prev, v->u.var.vidx)->vd.kind;
+    lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->vd.name));
   }
   }
   else {
   else {
     up->instack = 0;
     up->instack = 0;
     up->idx = cast_byte(v->u.info);
     up->idx = cast_byte(v->u.info);
-    up->ro = prev->f->upvalues[v->u.info].ro;
+    up->kind = prev->f->upvalues[v->u.info].kind;
     lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
     lua_assert(eqstr(name, prev->f->upvalues[v->u.info].name));
   }
   }
   up->name = name;
   up->name = name;
@@ -373,11 +375,17 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
 ** Look for an active local variable with the name 'n' in the
 ** Look for an active local variable with the name 'n' in the
 ** function 'fs'.
 ** function 'fs'.
 */
 */
-static int searchvar (FuncState *fs, TString *n) {
+static int searchvar (FuncState *fs, TString *n, expdesc *var) {
   int i;
   int i;
   for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
   for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
-    if (eqstr(n, getlocalvardesc(fs, i)->name))
-      return i;
+    Vardesc *vd = getlocalvardesc(fs, i);
+    if (eqstr(n, vd->vd.name)) {  /* found? */
+      if (vd->vd.kind == RDKCTC)  /* compile-time constant? */
+        init_exp(var, VCONST, fs->firstlocal + i);
+      else  /* real variable */
+        init_var(fs, var, i);
+      return var->k;
+    }
   }
   }
   return -1;  /* not found */
   return -1;  /* not found */
 }
 }
@@ -405,20 +413,19 @@ static void singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
   if (fs == NULL)  /* no more levels? */
   if (fs == NULL)  /* no more levels? */
     init_exp(var, VVOID, 0);  /* default is global */
     init_exp(var, VVOID, 0);  /* default is global */
   else {
   else {
-    int v = searchvar(fs, n);  /* look up locals at current level */
+    int v = searchvar(fs, n, var);  /* look up locals at current level */
     if (v >= 0) {  /* found? */
     if (v >= 0) {  /* found? */
-      init_var(fs, var, v);  /* variable is local */
-      if (!base)
+      if (v == VLOCAL && !base)
         markupval(fs, var->u.var.vidx);  /* local will be used as an upval */
         markupval(fs, var->u.var.vidx);  /* local will be used as an upval */
     }
     }
     else {  /* not found as local at current level; try upvalues */
     else {  /* not found as local at current level; try upvalues */
       int idx = searchupvalue(fs, n);  /* try existing upvalues */
       int idx = searchupvalue(fs, n);  /* try existing upvalues */
       if (idx < 0) {  /* not found? */
       if (idx < 0) {  /* not found? */
         singlevaraux(fs->prev, n, var, 0);  /* try upper levels */
         singlevaraux(fs->prev, n, var, 0);  /* try upper levels */
-        if (var->k == VVOID)  /* not found? */
-          return;  /* it is a global */
-        /* else was LOCAL or UPVAL */
-        idx  = newupvalue(fs, n, var);  /* will be a new upvalue */
+        if (var->k == VLOCAL || var->k == VUPVAL)  /* local or upvalue? */
+          idx  = newupvalue(fs, n, var);  /* will be a new upvalue */
+        else  /* it is a global or a constant */
+          return;  /* don't need to do anything at this level */
       }
       }
       init_exp(var, VUPVAL, idx);  /* new or old upvalue */
       init_exp(var, VUPVAL, idx);  /* new or old upvalue */
     }
     }
@@ -483,7 +490,7 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
 ** local variable.
 ** local variable.
 */
 */
 static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
 static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
-  const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->name);
+  const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->vd.name);
   const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
   const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
   luaK_semerror(ls, msg);  /* raise the error */
   luaK_semerror(ls, msg);  /* raise the error */
@@ -1710,21 +1717,20 @@ static int getlocalattribute (LexState *ls) {
     const char *attr = getstr(str_checkname(ls));
     const char *attr = getstr(str_checkname(ls));
     checknext(ls, '>');
     checknext(ls, '>');
     if (strcmp(attr, "const") == 0)
     if (strcmp(attr, "const") == 0)
-      return 1;  /* read-only variable */
+      return RDKCONST;  /* read-only variable */
     else if (strcmp(attr, "toclose") == 0)
     else if (strcmp(attr, "toclose") == 0)
-      return 2;  /* to-be-closed variable */
+      return RDKTOCLOSE;  /* to-be-closed variable */
     else
     else
       luaK_semerror(ls,
       luaK_semerror(ls,
         luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
         luaO_pushfstring(ls->L, "unknown attribute '%s'", attr));
   }
   }
-  return 0;
+  return VDKREG;
 }
 }
 
 
 
 
-static void checktoclose (LexState *ls, int toclose) {
-  if (toclose != -1) {  /* is there a to-be-closed variable? */
+static void checktoclose (LexState *ls, int level) {
+  if (level != -1) {  /* is there a to-be-closed variable? */
     FuncState *fs = ls->fs;
     FuncState *fs = ls->fs;
-    int level = luaY_nvarstack(fs) + toclose;
     markupval(fs, level + 1);
     markupval(fs, level + 1);
     fs->bl->insidetbc = 1;  /* in the scope of a to-be-closed variable */
     fs->bl->insidetbc = 1;  /* in the scope of a to-be-closed variable */
     luaK_codeABC(fs, OP_TBC, level, 0, 0);
     luaK_codeABC(fs, OP_TBC, level, 0, 0);
@@ -1734,20 +1740,20 @@ static void checktoclose (LexState *ls, int toclose) {
 
 
 static void localstat (LexState *ls) {
 static void localstat (LexState *ls) {
   /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
   /* stat -> LOCAL ATTRIB NAME {',' ATTRIB NAME} ['=' explist] */
+  FuncState *fs = ls->fs;
   int toclose = -1;  /* index of to-be-closed variable (if any) */
   int toclose = -1;  /* index of to-be-closed variable (if any) */
+  Vardesc *var;  /* last variable */
   int nvars = 0;
   int nvars = 0;
   int nexps;
   int nexps;
   expdesc e;
   expdesc e;
   do {
   do {
     int kind = getlocalattribute(ls);
     int kind = getlocalattribute(ls);
-    Vardesc *var = new_localvar(ls, str_checkname(ls));
-    if (kind != 0) {  /* is there an attribute? */
-      var->ro = 1;  /* all attributes make variable read-only */
-      if (kind == 2) {  /* to-be-closed? */
-        if (toclose != -1)  /* one already present? */
-          luaK_semerror(ls, "multiple to-be-closed variables in local list");
-        toclose = nvars;
-      }
+    var = new_localvar(ls, str_checkname(ls));
+    var->vd.kind = kind;
+    if (kind == RDKTOCLOSE) {  /* to-be-closed? */
+      if (toclose != -1)  /* one already present? */
+        luaK_semerror(ls, "multiple to-be-closed variables in local list");
+      toclose = luaY_nvarstack(fs) + nvars;
     }
     }
     nvars++;
     nvars++;
   } while (testnext(ls, ','));
   } while (testnext(ls, ','));
@@ -1757,9 +1763,18 @@ static void localstat (LexState *ls) {
     e.k = VVOID;
     e.k = VVOID;
     nexps = 0;
     nexps = 0;
   }
   }
-  adjust_assign(ls, nvars, nexps, &e);
+  if (nvars == nexps &&  /* no adjustments? */
+      var->vd.kind == RDKCONST &&  /* last variable is const? */
+      luaK_exp2const(fs, &e, &var->k)) {  /* compile-time constant? */
+    var->vd.kind = RDKCTC;  /* variable is a compile-time constant */
+    adjustlocalvars(ls, nvars - 1);  /* exclude last variable */
+    fs->nactvar++;  /* but count it */
+  }
+  else {
+    adjust_assign(ls, nvars, nexps, &e);
+    adjustlocalvars(ls, nvars);
+  }
   checktoclose(ls, toclose);
   checktoclose(ls, toclose);
-  adjustlocalvars(ls, nvars);
 }
 }
 
 
 
 
@@ -1925,7 +1940,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
   env = allocupvalue(fs);  /* ...set environment upvalue */
   env = allocupvalue(fs);  /* ...set environment upvalue */
   env->instack = 1;
   env->instack = 1;
   env->idx = 0;
   env->idx = 0;
-  env->ro = 0;
+  env->kind = VDKREG;
   env->name = ls->envn;
   env->name = ls->envn;
   luaX_next(ls);  /* read first token */
   luaX_next(ls);  /* read first token */
   statlist(ls);  /* parse main body */
   statlist(ls);  /* parse main body */

+ 17 - 10
lparser.h

@@ -34,8 +34,9 @@ typedef enum {
   VNONRELOC,  /* expression has its value in a fixed register;
   VNONRELOC,  /* expression has its value in a fixed register;
                  info = result register */
                  info = result register */
   VLOCAL,  /* local variable; var.ridx = local register;
   VLOCAL,  /* local variable; var.ridx = local register;
-              var.vidx = index in 'actvar.arr'  */
+              var.vidx = relative index in 'actvar.arr'  */
   VUPVAL,  /* upvalue variable; info = index of upvalue in 'upvalues' */
   VUPVAL,  /* upvalue variable; info = index of upvalue in 'upvalues' */
+  VCONST,  /* compile-time constant; info = absolute index in 'actvar.arr'  */
   VINDEXED,  /* indexed variable;
   VINDEXED,  /* indexed variable;
                 ind.t = table register;
                 ind.t = table register;
                 ind.idx = key's R index */
                 ind.idx = key's R index */
@@ -81,19 +82,25 @@ typedef struct expdesc {
 } expdesc;
 } expdesc;
 
 
 
 
+/* kinds of variables */
+#define VDKREG		0   /* regular */
+#define RDKCONST	1   /* constant */
+#define RDKTOCLOSE	2   /* to-be-closed */
+#define RDKCTC		3   /* compile-time constant */
+
 /* description of an active local variable */
 /* description of an active local variable */
-typedef struct Vardesc {
-  TValuefields;  /* constant value (if it is a compile-time constant) */
-  lu_byte ro;  /* true if variable is 'const' */
-  lu_byte sidx;  /* index of the variable in the stack */
-  short pidx;  /* index of the variable in the Proto's 'locvars' array */
-  TString *name;  /* variable name */
+typedef union Vardesc {
+  struct {
+    TValuefields;  /* constant value (if it is a compile-time constant) */
+    lu_byte kind;
+    lu_byte sidx;  /* index of the variable in the stack */
+    short pidx;  /* index of the variable in the Proto's 'locvars' array */
+    TString *name;  /* variable name */
+  } vd;
+  TValue k;  /* constant value (if any) */
 } Vardesc;
 } Vardesc;
 
 
 
 
-/* check whether Vardesc is in the stack (not a compile-time constant) */
-#define vdinstack(vd)	(ttisnil(vd))
-
 
 
 /* description of pending goto statements and label statements */
 /* description of pending goto statements and label statements */
 typedef struct Labeldesc {
 typedef struct Labeldesc {

+ 2 - 3
lundump.c

@@ -198,12 +198,11 @@ static void LoadUpvalues (LoadState *S, Proto *f) {
   n = LoadInt(S);
   n = LoadInt(S);
   f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
   f->upvalues = luaM_newvectorchecked(S->L, n, Upvaldesc);
   f->sizeupvalues = n;
   f->sizeupvalues = n;
-  for (i = 0; i < n; i++)
-    f->upvalues[i].name = NULL;
   for (i = 0; i < n; i++) {
   for (i = 0; i < n; i++) {
+    f->upvalues[i].name = NULL;
     f->upvalues[i].instack = LoadByte(S);
     f->upvalues[i].instack = LoadByte(S);
     f->upvalues[i].idx = LoadByte(S);
     f->upvalues[i].idx = LoadByte(S);
-    f->upvalues[i].ro = LoadByte(S);
+    f->upvalues[i].kind = LoadByte(S);
   }
   }
 }
 }
 
 

+ 19 - 12
manual/manual.of

@@ -223,7 +223,7 @@ In Lua, the global variable @Lid{_G} is initialized with this same value.
 so changing its value will affect only your own code.)
 so changing its value will affect only your own code.)
 
 
 When Lua loads a chunk,
 When Lua loads a chunk,
-the default value for its @id{_ENV} upvalue
+the default value for its @id{_ENV} variable
 is the global environment @seeF{load}.
 is the global environment @seeF{load}.
 Therefore, by default,
 Therefore, by default,
 free names in Lua code refer to entries in the global environment
 free names in Lua code refer to entries in the global environment
@@ -233,7 +233,7 @@ and some functions there operate on that environment.
 You can use @Lid{load} (or @Lid{loadfile})
 You can use @Lid{load} (or @Lid{loadfile})
 to load a chunk with a different environment.
 to load a chunk with a different environment.
 (In C, you have to load the chunk and then change the value
 (In C, you have to load the chunk and then change the value
-of its first upvalue.)
+of its first upvalue; see @See{lua_setupvalue}.)
 
 
 }
 }
 
 
@@ -1224,7 +1224,7 @@ As such, chunks can define local variables,
 receive arguments, and return values.
 receive arguments, and return values.
 Moreover, such anonymous function is compiled as in the
 Moreover, such anonymous function is compiled as in the
 scope of an external local variable called @id{_ENV} @see{globalenv}.
 scope of an external local variable called @id{_ENV} @see{globalenv}.
-The resulting function always has @id{_ENV} as its only upvalue,
+The resulting function always has @id{_ENV} as its only external variable,
 even if it does not use that variable.
 even if it does not use that variable.
 
 
 A chunk can be stored in a file or in a string inside the host program.
 A chunk can be stored in a file or in a string inside the host program.
@@ -2241,8 +2241,8 @@ and so the second @id{x} refers to the outside variable.
 Because of the @x{lexical scoping} rules,
 Because of the @x{lexical scoping} rules,
 local variables can be freely accessed by functions
 local variables can be freely accessed by functions
 defined inside their scope.
 defined inside their scope.
-A local variable used by an inner function is called
-an @def{upvalue}, or @emphx{external local variable},
+A local variable used by an inner function is called an @def{upvalue}
+(or @emphx{external local variable}, or simply @emphx{external variable})
 inside the inner function.
 inside the inner function.
 
 
 Notice that each execution of a @Rw{local} statement
 Notice that each execution of a @Rw{local} statement
@@ -4765,11 +4765,7 @@ and returns its name.
 Returns @id{NULL} (and pushes nothing)
 Returns @id{NULL} (and pushes nothing)
 when the index @id{n} is greater than the number of upvalues.
 when the index @id{n} is greater than the number of upvalues.
 
 
-For @N{C functions}, this function uses the empty string @T{""}
-as a name for all upvalues.
-(For Lua functions,
-upvalues are the external local variables that the function uses,
-and that are consequently included in its closure.)
+See @Lid{debug.getupvalue} for more information about upvalues.
 
 
 }
 }
 
 
@@ -8485,6 +8481,8 @@ The first parameter or local variable has @N{index 1}, and so on,
 following the order that they are declared in the code,
 following the order that they are declared in the code,
 counting only the variables that are active
 counting only the variables that are active
 in the current scope of the function.
 in the current scope of the function.
+Compile-time constants may not appear in this listing,
+if they were optimized away by the compiler.
 Negative indices refer to vararg arguments;
 Negative indices refer to vararg arguments;
 @num{-1} is the first vararg argument.
 @num{-1} is the first vararg argument.
 The function returns @nil if there is no variable with the given index,
 The function returns @nil if there is no variable with the given index,
@@ -8520,8 +8518,15 @@ This function returns the name and the value of the upvalue
 with index @id{up} of the function @id{f}.
 with index @id{up} of the function @id{f}.
 The function returns @nil if there is no upvalue with the given index.
 The function returns @nil if there is no upvalue with the given index.
 
 
-Variable names starting with @Char{(} (open parenthesis) @C{)}
-represent variables with no known names
+(For Lua functions,
+upvalues are the external local variables that the function uses,
+and that are consequently included in its closure.)
+
+For @N{C functions}, this function uses the empty string @T{""}
+as a name for all upvalues.
+
+Variable name @Char{?} (interrogation mark)
+represents variables with no known names
 (variables from chunks saved without debug information).
 (variables from chunks saved without debug information).
 
 
 }
 }
@@ -8626,6 +8631,8 @@ The function returns @nil if there is no upvalue
 with the given index.
 with the given index.
 Otherwise, it returns the name of the upvalue.
 Otherwise, it returns the name of the upvalue.
 
 
+See @Lid{debug.getupvalue} for more information about upvalues.
+
 }
 }
 
 
 @LibEntry{debug.setuservalue (udata, value, n)|
 @LibEntry{debug.setuservalue (udata, value, n)|

+ 58 - 31
testes/code.lua

@@ -7,6 +7,22 @@ if T==nil then
 end
 end
 print "testing code generation and optimizations"
 print "testing code generation and optimizations"
 
 
+-- to test constant propagation
+local <const> k0 = 0
+local <const> k1 = 1
+local <const> k3 = 3
+local <const> k6 = k3 + (k3 << k0)
+local <const> kFF0 = 0xFF0
+local <const> k3_78 = 3.78
+local <const> x, <const> k3_78_4 = 10, k3_78 / 4
+assert(x == 10)
+
+local <const> kx = "x"
+
+local <const> kTrue = true
+local <const> kFalse = false
+
+local <const> kNil = nil
 
 
 -- this code gave an error for the code checker
 -- this code gave an error for the code checker
 do
 do
@@ -27,12 +43,12 @@ end
 
 
 local function foo ()
 local function foo ()
   local a
   local a
-  a = 3;
+  a = k3;
   a = 0; a = 0.0; a = -7 + 7
   a = 0; a = 0.0; a = -7 + 7
-  a = 3.78/4; a = 3.78/4
-  a = -3.78/4; a = 3.78/4; a = -3.78/4
+  a = k3_78/4; a = k3_78_4
+  a = -k3_78/4; a = k3_78/4; a = -3.78/4
   a = -3.79/4; a = 0.0; a = -0;
   a = -3.79/4; a = 0.0; a = -0;
-  a = 3; a = 3.0; a = 3; a = 3.0
+  a = k3; a = 3.0; a = 3; a = 3.0
 end
 end
 
 
 checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
 checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
@@ -86,10 +102,11 @@ end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
 
 
 -- sequence of LOADNILs
 -- sequence of LOADNILs
 check(function ()
 check(function ()
+  local <const> kNil = nil
   local a,b,c
   local a,b,c
   local d; local e;
   local d; local e;
   local f,g,h;
   local f,g,h;
-  d = nil; d=nil; b=nil; a=nil; c=nil;
+  d = nil; d=nil; b=nil; a=kNil; c=nil;
 end, 'LOADNIL', 'RETURN0')
 end, 'LOADNIL', 'RETURN0')
 
 
 check(function ()
 check(function ()
@@ -109,7 +126,7 @@ check (function (a,b,c) return a end, 'RETURN1')
 
 
 
 
 -- infinite loops
 -- infinite loops
-check(function () while true do local a = -1 end end,
+check(function () while kTrue do local a = -1 end end,
 'LOADI', 'JMP', 'RETURN0')
 'LOADI', 'JMP', 'RETURN0')
 
 
 check(function () while 1 do local a = -1 end end,
 check(function () while 1 do local a = -1 end end,
@@ -125,9 +142,9 @@ check(function (a,b,c,d) return a..b..c..d end,
 
 
 -- not
 -- not
 check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
 check(function () return not not nil end, 'LOADBOOL', 'RETURN1')
-check(function () return not not false end, 'LOADBOOL', 'RETURN1')
+check(function () return not not kFalse end, 'LOADBOOL', 'RETURN1')
 check(function () return not not true end, 'LOADBOOL', 'RETURN1')
 check(function () return not not true end, 'LOADBOOL', 'RETURN1')
-check(function () return not not 1 end, 'LOADBOOL', 'RETURN1')
+check(function () return not not k3 end, 'LOADBOOL', 'RETURN1')
 
 
 -- direct access to locals
 -- direct access to locals
 check(function ()
 check(function ()
@@ -144,7 +161,8 @@ end,
 -- direct access to constants
 -- direct access to constants
 check(function ()
 check(function ()
   local a,b
   local a,b
-  a.x = 3.2
+  local c = kNil
+  a[kx] = 3.2
   a.x = b
   a.x = b
   a[b] = 'x'
   a[b] = 'x'
 end,
 end,
@@ -152,8 +170,9 @@ end,
 
 
 -- "get/set table" with numeric indices
 -- "get/set table" with numeric indices
 check(function (a)
 check(function (a)
+  local <const> k255 = 255
   a[1] = a[100]
   a[1] = a[100]
-  a[255] = a[256]
+  a[k255] = a[256]
   a[256] = 5
   a[256] = 5
 end,
 end,
   'GETI', 'SETI',
   'GETI', 'SETI',
@@ -170,7 +189,7 @@ end,
 
 
 check(function ()
 check(function ()
   local a,b
   local a,b
-  a[true] = false
+  a[kTrue] = false
 end,
 end,
   'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
   'LOADNIL', 'LOADBOOL', 'SETTABLE', 'RETURN0')
 
 
@@ -238,37 +257,39 @@ local function checkF (func, val)
 end
 end
 
 
 checkF(function () return 0.0 end, 0.0)
 checkF(function () return 0.0 end, 0.0)
-checkI(function () return 0 end, 0)
-checkI(function () return -0//1 end, 0)
+checkI(function () return k0 end, 0)
+checkI(function () return -k0//1 end, 0)
 checkK(function () return 3^-1 end, 1/3)
 checkK(function () return 3^-1 end, 1/3)
 checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
 checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
 checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
 checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
-checkF(function () return (-3^0 + 5) // 3.0 end, 1.0)
-checkI(function () return -3 % 5 end, 2)
+checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
+checkI(function () return -k3 % 5 end, 2)
 checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
 checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
 checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
 checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
 checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
 checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
-checkI(function () return ~(~0xFF0 | 0xFF0) end, 0)
+checkI(function () return ~(~kFF0 | kFF0) end, 0)
 checkI(function () return ~~-1024.0 end, -1024)
 checkI(function () return ~~-1024.0 end, -1024)
-checkI(function () return ((100 << 6) << -4) >> 2 end, 100)
+checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
 
 
 -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
 -- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
 local a = 17; local sbx = ((1 << a) - 1) >> 1   -- avoid folding
 local a = 17; local sbx = ((1 << a) - 1) >> 1   -- avoid folding
-checkI(function () return 65535 end, sbx)
-checkI(function () return -65535 end, -sbx)
-checkI(function () return 65536 end, sbx + 1)
-checkK(function () return 65537 end, sbx + 2)
-checkK(function () return -65536 end, -(sbx + 1))
+local <const> border = 65535
+checkI(function () return border end, sbx)
+checkI(function () return -border end, -sbx)
+checkI(function () return border + 1 end, sbx + 1)
+checkK(function () return border + 2 end, sbx + 2)
+checkK(function () return -(border + 1) end, -(sbx + 1))
 
 
-checkF(function () return 65535.0 end, sbx + 0.0)
-checkF(function () return -65535.0 end, -sbx + 0.0)
-checkF(function () return 65536.0 end, (sbx + 1.0))
-checkK(function () return 65537.0 end, (sbx + 2.0))
-checkK(function () return -65536.0 end, -(sbx + 1.0))
+local <const> border = 65535.0
+checkF(function () return border end, sbx + 0.0)
+checkF(function () return -border end, -sbx + 0.0)
+checkF(function () return border + 1 end, (sbx + 1.0))
+checkK(function () return border + 2 end, (sbx + 2.0))
+checkK(function () return -(border + 1) end, -(sbx + 1.0))
 
 
 
 
 -- immediate operands
 -- immediate operands
-checkR(function (x) return x + 1 end, 10, 11, 'ADDI', 'RETURN1')
+checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'RETURN1')
 checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
 checkR(function (x) return 128 + x end, 0.0, 128.0, 'ADDI', 'RETURN1')
 checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
 checkR(function (x) return x * -127 end, -1.0, 127.0, 'MULI', 'RETURN1')
 checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
 checkR(function (x) return 20 * x end, 2, 40, 'MULI', 'RETURN1')
@@ -276,7 +297,7 @@ checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWI', 'RETURN1')
 checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
 checkR(function (x) return x / 40 end, 40, 1.0, 'DIVI', 'RETURN1')
 checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
 checkR(function (x) return x // 1 end, 10.0, 10.0, 'IDIVI', 'RETURN1')
 checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
 checkR(function (x) return x % (100 - 10) end, 91, 1, 'MODI', 'RETURN1')
-checkR(function (x) return 1 << x end, 3, 8, 'SHLI', 'RETURN1')
+checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'RETURN1')
 checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
 checkR(function (x) return x << 2 end, 10, 40, 'SHRI', 'RETURN1')
 checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
 checkR(function (x) return x >> 2 end, 8, 2, 'SHRI', 'RETURN1')
 checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
 checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'RETURN1')
@@ -295,7 +316,7 @@ checkR(function (x) return x % (100.0 - 10) end, 91, 1.0, 'MODK', 'RETURN1')
 
 
 -- no foldings (and immediate operands)
 -- no foldings (and immediate operands)
 check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
 check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
-check(function () return 3/0 end, 'LOADI', 'DIVI', 'RETURN1')
+check(function () return k3/0 end, 'LOADI', 'DIVI', 'RETURN1')
 check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
 check(function () return 0%0 end, 'LOADI', 'MODI', 'RETURN1')
 check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
 check(function () return -4//0 end, 'LOADI', 'IDIVI', 'RETURN1')
 check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
 check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'RETURN1')
@@ -335,7 +356,7 @@ end,
 
 
 do   -- tests for table access in upvalues
 do   -- tests for table access in upvalues
   local t
   local t
-  check(function () t.x = t.y end, 'GETTABUP', 'SETTABUP')
+  check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
   check(function (a) t[a()] = t[a()] end,
   check(function (a) t[a()] = t[a()] end,
   'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
   'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
   'GETUPVAL', 'GETTABLE', 'SETTABLE')
   'GETUPVAL', 'GETTABLE', 'SETTABLE')
@@ -379,6 +400,12 @@ function (a)
 end
 end
 )
 )
 
 
+checkequal(function () return 6 or true or nil end,
+           function () return k6 or kTrue or kNil end)
+
+checkequal(function () return 6 and true or nil end,
+           function () return k6 and kTrue or kNil end)
+
 
 
 print 'OK'
 print 'OK'
 
 

+ 21 - 3
testes/constructs.lua

@@ -287,7 +287,7 @@ a,b = F(nil)==nil; assert(a == true and b == nil)
 ------------------------------------------------------------------
 ------------------------------------------------------------------
 
 
 -- sometimes will be 0, sometimes will not...
 -- sometimes will be 0, sometimes will not...
-_ENV.GLOB1 = math.floor(os.time()) % 2
+_ENV.GLOB1 = math.random(0, 1)
 
 
 -- basic expressions with their respective values
 -- basic expressions with their respective values
 local basiccases = {
 local basiccases = {
@@ -298,6 +298,26 @@ local basiccases = {
   {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
   {"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
 }
 }
 
 
+local prog
+
+if _ENV.GLOB1 == 0 then
+  basiccases[2][1] = "F"   -- constant false
+
+  prog = [[
+    local <const> F = false
+    if %s then IX = true end
+    return %s
+]]
+else
+  basiccases[4][1] = "k10"   -- constant 10
+
+  prog = [[
+    local <const> k10 = 10
+    if %s then IX = true end
+    return %s
+  ]]
+end
+
 print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
 print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
 
 
 
 
@@ -337,8 +357,6 @@ cases[1] = basiccases
 for i = 2, level do cases[i] = createcases(i) end
 for i = 2, level do cases[i] = createcases(i) end
 print("+")
 print("+")
 
 
-local prog = [[if %s then IX = true end; return %s]]
-
 local i = 0
 local i = 0
 for n = 1, level do
 for n = 1, level do
   for _, v in pairs(cases[n]) do
   for _, v in pairs(cases[n]) do

+ 1 - 1
testes/locals.lua

@@ -324,7 +324,7 @@ do
 
 
   -- errors due to non-closable values
   -- errors due to non-closable values
   local function foo ()
   local function foo ()
-    local <toclose> x = 34
+    local <toclose> x = {}
   end
   end
   local stat, msg = pcall(foo)
   local stat, msg = pcall(foo)
   assert(not stat and string.find(msg, "variable 'x'"))
   assert(not stat and string.find(msg, "variable 'x'"))

+ 9 - 7
testes/math.lua

@@ -270,7 +270,7 @@ else
 end
 end
 
 
 do
 do
-  local NaN = 0/0
+  local <const> NaN = 0/0
   assert(not (NaN < 0))
   assert(not (NaN < 0))
   assert(not (NaN > minint))
   assert(not (NaN > minint))
   assert(not (NaN <= -9))
   assert(not (NaN <= -9))
@@ -767,7 +767,8 @@ assert(a == '10' and b == '20')
 
 
 do
 do
   print("testing -0 and NaN")
   print("testing -0 and NaN")
-  local mz, z = -0.0, 0.0
+  local <const> mz = -0.0
+  local <const> z = 0.0
   assert(mz == z)
   assert(mz == z)
   assert(1/mz < 0 and 0 < 1/z)
   assert(1/mz < 0 and 0 < 1/z)
   local a = {[mz] = 1}
   local a = {[mz] = 1}
@@ -775,17 +776,18 @@ do
   a[z] = 2
   a[z] = 2
   assert(a[z] == 2 and a[mz] == 2)
   assert(a[z] == 2 and a[mz] == 2)
   local inf = math.huge * 2 + 1
   local inf = math.huge * 2 + 1
-  mz, z = -1/inf, 1/inf
+  local <const> mz = -1/inf
+  local <const> z = 1/inf
   assert(mz == z)
   assert(mz == z)
   assert(1/mz < 0 and 0 < 1/z)
   assert(1/mz < 0 and 0 < 1/z)
-  local NaN = inf - inf
+  local <const> NaN = inf - inf
   assert(NaN ~= NaN)
   assert(NaN ~= NaN)
   assert(not (NaN < NaN))
   assert(not (NaN < NaN))
   assert(not (NaN <= NaN))
   assert(not (NaN <= NaN))
   assert(not (NaN > NaN))
   assert(not (NaN > NaN))
   assert(not (NaN >= NaN))
   assert(not (NaN >= NaN))
   assert(not (0 < NaN) and not (NaN < 0))
   assert(not (0 < NaN) and not (NaN < 0))
-  local NaN1 = 0/0
+  local <const> NaN1 = 0/0
   assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
   assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
   local a = {}
   local a = {}
   assert(not pcall(rawset, a, NaN, 1))
   assert(not pcall(rawset, a, NaN, 1))
@@ -814,8 +816,8 @@ end
 -- the first call after seed 1007 should return 0x7a7040a5a323c9d6
 -- the first call after seed 1007 should return 0x7a7040a5a323c9d6
 do
 do
   -- all computations assume at most 32-bit integers
   -- all computations assume at most 32-bit integers
-  local h = 0x7a7040a5   -- higher half
-  local l = 0xa323c9d6   -- lower half
+  local <const> h = 0x7a7040a5   -- higher half
+  local <const> l = 0xa323c9d6   -- lower half
 
 
   math.randomseed(1007)
   math.randomseed(1007)
   -- get the low 'intbits' of the 64-bit expected result
   -- get the low 'intbits' of the 64-bit expected result