Towards constant propagation

This commit detaches the number of active variables from the
number of variables in the stack, during compilation. Soon,
compile-time constants will be propagated and therefore will
not exist during run time (in the stack).
diff --git a/lcode.c b/lcode.c
index cb6ea0d..837253f 100644
--- a/lcode.c
+++ b/lcode.c
@@ -458,7 +458,7 @@
 )
 */
 static void freereg (FuncState *fs, int reg) {
-  if (reg >= fs->nactvar) {
+  if (reg >= luaY_nvarstack(fs)) {
     fs->freereg--;
     lua_assert(reg == fs->freereg);
   }
@@ -850,7 +850,7 @@
   if (e->k == VNONRELOC) {  /* expression already has a register? */
     if (!hasjumps(e))  /* no jumps? */
       return e->u.info;  /* result is already in a register */
-    if (e->u.info >= fs->nactvar) {  /* reg. is not a local? */
+    if (e->u.info >= luaY_nvarstack(fs)) {  /* reg. is not a local? */
       exp2reg(fs, e, e->u.info);  /* put final result in it */
       return e->u.info;
     }
diff --git a/lparser.c b/lparser.c
index 1551cda..c4626ba 100644
--- a/lparser.c
+++ b/lparser.c
@@ -173,13 +173,13 @@
 static int registerlocalvar (lua_State *L, FuncState *fs, TString *varname) {
   Proto *f = fs->f;
   int oldsize = f->sizelocvars;
-  luaM_growvector(L, f->locvars, fs->nlocvars, f->sizelocvars,
+  luaM_growvector(L, f->locvars, fs->ndebugvars, f->sizelocvars,
                   LocVar, SHRT_MAX, "local variables");
   while (oldsize < f->sizelocvars)
     f->locvars[oldsize++].varname = NULL;
-  f->locvars[fs->nlocvars].varname = varname;
+  f->locvars[fs->ndebugvars].varname = varname;
   luaC_objbarrier(L, f, varname);
-  return fs->nlocvars++;
+  return fs->ndebugvars++;
 }
 
 
@@ -193,12 +193,13 @@
   Vardesc *var;
   int reg = registerlocalvar(L, fs, name);
   checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
-                  MAXVARS, "local variables");
+                 MAXVARS, "local variables");
   luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
                   dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
   var = &dyd->actvar.arr[dyd->actvar.n++];
   var->pidx = cast(short, reg);
   var->ro = 0;
+  var->name = name;
   setnilvalue(var);
   return var;
 }
@@ -218,12 +219,41 @@
 
 
 /*
+** Convert 'nvar' (number of active variables at some point) to
+** number of variables in the stack at that point.
+*/
+static int stacklevel (FuncState *fs, int nvar) {
+  while (nvar > 0) {
+    Vardesc *vd = getlocalvardesc(fs, nvar - 1);
+    if (vdinstack(vd))  /* is in the stack? */
+      return vd->sidx + 1;
+    else
+      nvar--;  /* try previous variable */
+  }
+  return 0;  /* no variables */
+}
+
+
+/*
+** Return the number of variables in the stack for function 'fs'
+*/
+int luaY_nvarstack (FuncState *fs) {
+  return stacklevel(fs, fs->nactvar);
+}
+
+
+/*
 ** Get the debug-information entry for current variable 'i'.
 */
 static LocVar *localdebuginfo (FuncState *fs, int i) {
-  int idx = getlocalvardesc(fs, i)->pidx;
-  lua_assert(idx < fs->nlocvars);
-  return &fs->f->locvars[idx];
+  Vardesc *vd = getlocalvardesc(fs, i);
+  if (!vdinstack(vd))
+    return NULL;  /* no debug info. for constants */
+  else {
+    int idx = vd->pidx;
+    lua_assert(idx < fs->ndebugvars);
+    return &fs->f->locvars[idx];
+  }
 }
 
 
@@ -242,7 +272,7 @@
     case VLOCAL: {
       Vardesc *vardesc = getlocalvardesc(fs, e->u.var.vidx);
       if (vardesc->ro)
-        varname = fs->f->locvars[vardesc->pidx].varname;
+        varname = vardesc->name;
       break;
     }
     case VUPVAL: {
@@ -267,11 +297,12 @@
 */
 static void adjustlocalvars (LexState *ls, int nvars) {
   FuncState *fs = ls->fs;
+  int stklevel = luaY_nvarstack(fs);
   int i;
   for (i = 0; i < nvars; i++) {
     int varidx = fs->nactvar++;
     Vardesc *var = getlocalvardesc(fs, varidx);
-    var->sidx = varidx;
+    var->sidx = stklevel++;
     fs->f->locvars[var->pidx].startpc = fs->pc;
   }
 }
@@ -283,8 +314,11 @@
 */
 static void removevars (FuncState *fs, int tolevel) {
   fs->ls->dyd->actvar.n -= (fs->nactvar - tolevel);
-  while (fs->nactvar > tolevel)
-    localdebuginfo(fs, --fs->nactvar)->endpc = fs->pc;
+  while (fs->nactvar > tolevel) {
+    LocVar *var = localdebuginfo(fs, --fs->nactvar);
+    if (var)  /* does it have debug information? */
+      var->endpc = fs->pc;
+  }
 }
 
 
@@ -321,7 +355,7 @@
     up->instack = 1;
     up->idx = v->u.var.sidx;
     up->ro = getlocalvardesc(prev, v->u.var.vidx)->ro;
-    lua_assert(eqstr(name, localdebuginfo(prev, v->u.var.vidx)->varname));
+    lua_assert(eqstr(name, getlocalvardesc(prev, v->u.var.vidx)->name));
   }
   else {
     up->instack = 0;
@@ -342,7 +376,7 @@
 static int searchvar (FuncState *fs, TString *n) {
   int i;
   for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
-    if (eqstr(n, localdebuginfo(fs, i)->varname))
+    if (eqstr(n, getlocalvardesc(fs, i)->name))
       return i;
   }
   return -1;  /* not found */
@@ -375,7 +409,7 @@
     if (v >= 0) {  /* found? */
       init_var(fs, var, v);  /* variable is local */
       if (!base)
-        markupval(fs, var->u.var.sidx);  /* 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 */
       int idx = searchupvalue(fs, n);  /* try existing upvalues */
@@ -449,7 +483,7 @@
 ** local variable.
 */
 static l_noret jumpscopeerror (LexState *ls, Labeldesc *gt) {
-  const char *varname = getstr(localdebuginfo(ls->fs, gt->nactvar)->varname);
+  const char *varname = getstr(getlocalvardesc(ls->fs, gt->nactvar)->name);
   const char *msg = "<goto %s> at line %d jumps into the scope of local '%s'";
   msg = luaO_pushfstring(ls->L, msg, getstr(gt->name), gt->line, varname);
   luaK_semerror(ls, msg);  /* raise the error */
@@ -552,7 +586,7 @@
     ll->arr[l].nactvar = fs->bl->nactvar;
   }
   if (solvegotos(ls, &ll->arr[l])) {  /* need close? */
-    luaK_codeABC(fs, OP_CLOSE, fs->nactvar, 0, 0);
+    luaK_codeABC(fs, OP_CLOSE, luaY_nvarstack(fs), 0, 0);
     return 1;
   }
   return 0;
@@ -568,10 +602,10 @@
   /* correct pending gotos to current block */
   for (i = bl->firstgoto; i < gl->n; i++) {  /* for each pending goto */
     Labeldesc *gt = &gl->arr[i];
-    if (gt->nactvar > bl->nactvar) {  /* leaving a variable scope? */
-      gt->nactvar = bl->nactvar;  /* update goto level */
+    /* leaving a variable scope? */
+    if (stacklevel(fs, gt->nactvar) > stacklevel(fs, bl->nactvar))
       gt->close |= bl->upval;  /* jump may need a close */
-    }
+    gt->nactvar = bl->nactvar;  /* update goto level */
   }
 }
 
@@ -585,7 +619,7 @@
   bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
   bl->previous = fs->bl;
   fs->bl = bl;
-  lua_assert(fs->freereg == fs->nactvar);
+  lua_assert(fs->freereg == luaY_nvarstack(fs));
 }
 
 
@@ -610,14 +644,15 @@
   BlockCnt *bl = fs->bl;
   LexState *ls = fs->ls;
   int hasclose = 0;
+  int stklevel = stacklevel(fs, bl->nactvar);  /* level outside the block */
   if (bl->isloop)  /* fix pending breaks? */
     hasclose = createlabel(ls, luaS_newliteral(ls->L, "break"), 0, 0);
   if (!hasclose && bl->previous && bl->upval)
-    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
+    luaK_codeABC(fs, OP_CLOSE, stklevel, 0, 0);
   fs->bl = bl->previous;
   removevars(fs, bl->nactvar);
   lua_assert(bl->nactvar == fs->nactvar);
-  fs->freereg = fs->nactvar;  /* free registers */
+  fs->freereg = stklevel;  /* free registers */
   ls->dyd->label.n = bl->firstlabel;  /* remove local labels */
   if (bl->previous)  /* inner block? */
     movegotosout(fs, bl);  /* update pending gotos to outer block */
@@ -675,7 +710,7 @@
   fs->nabslineinfo = 0;
   fs->np = 0;
   fs->nups = 0;
-  fs->nlocvars = 0;
+  fs->ndebugvars = 0;
   fs->nactvar = 0;
   fs->needclose = 0;
   fs->firstlocal = ls->dyd->actvar.n;
@@ -691,7 +726,7 @@
   lua_State *L = ls->L;
   FuncState *fs = ls->fs;
   Proto *f = fs->f;
-  luaK_ret(fs, fs->nactvar, 0);  /* final return */
+  luaK_ret(fs, luaY_nvarstack(fs), 0);  /* final return */
   leaveblock(fs);
   lua_assert(fs->bl == NULL);
   luaK_finish(fs);
@@ -701,7 +736,7 @@
                        fs->nabslineinfo, AbsLineInfo);
   luaM_shrinkvector(L, f->k, f->sizek, fs->nk, TValue);
   luaM_shrinkvector(L, f->p, f->sizep, fs->np, Proto *);
-  luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);
+  luaM_shrinkvector(L, f->locvars, f->sizelocvars, fs->ndebugvars, LocVar);
   luaM_shrinkvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
   ls->fs = fs->prev;
   luaC_checkGC(L);
@@ -1356,8 +1391,9 @@
     newgotoentry(ls, name, line, luaK_jump(fs));
   else {  /* found a label */
     /* backward jump; will be resolved here */
-    if (fs->nactvar > lb->nactvar)  /* leaving the scope of some variable? */
-      luaK_codeABC(fs, OP_CLOSE, lb->nactvar, 0, 0);
+    int lblevel = stacklevel(fs, lb->nactvar);  /* label level */
+    if (luaY_nvarstack(fs) > lblevel)  /* leaving the scope of a variable? */
+      luaK_codeABC(fs, OP_CLOSE, lblevel, 0, 0);
     /* create jump and link it to the label */
     luaK_patchlist(fs, luaK_jump(fs), lb->pc);
   }
@@ -1432,7 +1468,7 @@
   if (bl2.upval) {  /* upvalues? */
     int exit = luaK_jump(fs);  /* normal exit must jump over fix */
     luaK_patchtohere(fs, condexit);  /* repetition must close upvalues */
-    luaK_codeABC(fs, OP_CLOSE, bl2.nactvar, 0, 0);
+    luaK_codeABC(fs, OP_CLOSE, stacklevel(fs, bl2.nactvar), 0, 0);
     condexit = luaK_jump(fs);  /* repeat after closing upvalues */
     luaK_patchtohere(fs, exit);  /* normal exit comes to here */
   }
@@ -1532,7 +1568,6 @@
   /* create control variables */
   new_localvarliteral(ls, "(for generator)");
   new_localvarliteral(ls, "(for state)");
-  markupval(fs, fs->nactvar);  /* state may create an upvalue */
   new_localvarliteral(ls, "(for control)");
   new_localvarliteral(ls, "(for toclose)");
   /* create declared variables */
@@ -1545,6 +1580,7 @@
   line = ls->linenumber;
   adjust_assign(ls, 4, explist(ls, &e), &e);
   adjustlocalvars(ls, 4);  /* control variables */
+  markupval(fs, luaY_nvarstack(fs));  /* state may create an upvalue */
   luaK_checkstack(fs, 3);  /* extra space to call generator */
   forbody(ls, base, line, nvars - 4, 1);
 }
@@ -1587,7 +1623,8 @@
     TString *lname = ls->lookahead.seminfo.ts;  /* label's id */
     Labeldesc *lb = findlabel(ls, lname);
     if (lb) {  /* a backward jump? */
-      if (ls->fs->nactvar > lb->nactvar)  /* needs to close variables? */
+      /* does it need to close variables? */
+      if (luaY_nvarstack(ls->fs) > stacklevel(ls->fs, lb->nactvar))
         return 0;  /* not a single jump; cannot optimize */
       *target = lb->pc;
     }
@@ -1659,11 +1696,12 @@
 static void localfunc (LexState *ls) {
   expdesc b;
   FuncState *fs = ls->fs;
+  int fvar = fs->nactvar;  /* function's variable index */
   new_localvar(ls, str_checkname(ls));  /* new local variable */
   adjustlocalvars(ls, 1);  /* enter its scope */
   body(ls, &b, 0, ls->linenumber);  /* function created in next register */
   /* debug information will only see the variable after this point! */
-  localdebuginfo(fs, b.u.info)->startpc = fs->pc;
+  localdebuginfo(fs, fvar)->startpc = fs->pc;
 }
 
 
@@ -1687,9 +1725,10 @@
 static void checktoclose (LexState *ls, int toclose) {
   if (toclose != -1) {  /* is there a to-be-closed variable? */
     FuncState *fs = ls->fs;
-    markupval(fs, fs->nactvar + toclose + 1);
+    int level = luaY_nvarstack(fs) + toclose;
+    markupval(fs, level + 1);
     fs->bl->insidetbc = 1;  /* in the scope of a to-be-closed variable */
-    luaK_codeABC(fs, OP_TBC, fs->nactvar + toclose, 0, 0);
+    luaK_codeABC(fs, OP_TBC, level, 0, 0);
   }
 }
 
@@ -1773,7 +1812,7 @@
   FuncState *fs = ls->fs;
   expdesc e;
   int nret;  /* number of values being returned */
-  int first = fs->nactvar;  /* first slot to be returned */
+  int first = luaY_nvarstack(fs);  /* first slot to be returned */
   if (block_follow(ls, 1) || ls->t.token == ';')
     nret = 0;  /* return no values */
   else {
@@ -1782,7 +1821,7 @@
       luaK_setmultret(fs, &e);
       if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) {  /* tail call? */
         SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
-        lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
+        lua_assert(GETARG_A(getinstruction(fs,&e)) == luaY_nvarstack(fs));
       }
       nret = LUA_MULTRET;  /* return all values */
     }
@@ -1867,8 +1906,8 @@
     }
   }
   lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&
-             ls->fs->freereg >= ls->fs->nactvar);
-  ls->fs->freereg = ls->fs->nactvar;  /* free registers */
+             ls->fs->freereg >= luaY_nvarstack(ls->fs));
+  ls->fs->freereg = luaY_nvarstack(ls->fs);  /* free registers */
   leavelevel(ls);
 }
 
diff --git a/lparser.h b/lparser.h
index cc2ec14..7d43a81 100644
--- a/lparser.h
+++ b/lparser.h
@@ -83,19 +83,24 @@
 
 /* description of an active local variable */
 typedef struct Vardesc {
-  TValuefields;  /* constant value (if variable is 'const') */
+  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 */
 } Vardesc;
 
 
+/* check whether Vardesc is in the stack (not a compile-time constant) */
+#define vdinstack(vd)	(ttisnil(vd))
+
+
 /* description of pending goto statements and label statements */
 typedef struct Labeldesc {
   TString *name;  /* label identifier */
   int pc;  /* position in code */
   int line;  /* line where it appeared */
-  lu_byte nactvar;  /* local level where it appears in current block */
+  lu_byte nactvar;  /* number of active variables in that position */
   lu_byte close;  /* goto that escapes upvalues */
 } Labeldesc;
 
@@ -138,7 +143,7 @@
   int nabslineinfo;  /* number of elements in 'abslineinfo' */
   int firstlocal;  /* index of first local var (in Dyndata array) */
   int firstlabel;  /* index of first label (in 'dyd->label->arr') */
-  short nlocvars;  /* number of elements in 'f->locvars' */
+  short ndebugvars;  /* number of elements in 'f->locvars' */
   lu_byte nactvar;  /* number of active local variables */
   lu_byte nups;  /* number of upvalues */
   lu_byte freereg;  /* first free register */
@@ -147,6 +152,7 @@
 } FuncState;
 
 
+LUAI_FUNC int luaY_nvarstack (FuncState *fs);
 LUAI_FUNC LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
                                  Dyndata *dyd, const char *name, int firstchar);