Redesign of prototype generation, part 3: bc and lineinfo.

Use a growable, per-chunk bytecode instruction/line stack.
Collect bc/lineinfo for prototype at the end.
This commit is contained in:
Mike Pall 2010-02-08 05:28:57 +01:00
parent 48d93d8c84
commit 4424027844
3 changed files with 108 additions and 87 deletions

View File

@ -311,6 +311,8 @@ void lj_lex_setup(lua_State *L, LexState *ls)
ls->vstack = NULL;
ls->sizevstack = 0;
ls->vtop = 0;
ls->bcstack = NULL;
ls->sizebcstack = 0;
ls->lookahead = TK_eof; /* No look-ahead token. */
ls->linenumber = 1;
ls->lastline = 1;
@ -339,6 +341,7 @@ void lj_lex_setup(lua_State *L, LexState *ls)
void lj_lex_cleanup(lua_State *L, LexState *ls)
{
global_State *g = G(L);
lj_mem_freevec(g, ls->bcstack, ls->sizebcstack, BCInsLine);
lj_mem_freevec(g, ls->vstack, ls->sizevstack, VarInfo);
lj_str_freebuf(g, &ls->sb);
}

View File

@ -32,6 +32,12 @@ TKDEF(TKENUM1, TKENUM2)
typedef int LexToken;
/* Combined bytecode ins/line. Only used during bytecode generation. */
typedef struct BCInsLine {
BCIns ins; /* Bytecode instruction. */
BCLine line; /* Line number for this bytecode. */
} BCInsLine;
/* Lua lexer state. */
typedef struct LexState {
struct FuncState *fs; /* Current FuncState. Defined in lj_parse.c. */
@ -53,6 +59,8 @@ typedef struct LexState {
VarInfo *vstack; /* Stack for names and extents of local variables. */
MSize sizevstack; /* Size of variable stack. */
MSize vtop; /* Top of variable stack. */
BCInsLine *bcstack; /* Stack for bytecode instructions/line numbers. */
MSize sizebcstack; /* Size of bytecode stack. */
uint32_t level; /* Syntactical nesting level. */
} LexState;

View File

@ -110,11 +110,14 @@ typedef struct FuncState {
BCPos lasttarget; /* Bytecode position of last jump target. */
BCPos jpc; /* Pending jump list to next bytecode. */
BCReg freereg; /* First free register. */
BCReg nactvar; /* Number of active local variables. */
BCReg nkn, nkgc; /* Number of lua_Number/GCobj constants */
BCLine linedefined; /* First line of the function definition. */
BCInsLine *bcbase; /* Base of bytecode stack. */
BCPos bclim; /* Limit of bytecode stack. */
MSize vbase; /* Base of variable stack for this function. */
uint8_t nactvar; /* Number of active local variables. */
uint8_t flags; /* Prototype flags. */
uint8_t numparams; /* Number of active local variables. */
uint8_t framesize; /* Fixed frame size. */
uint8_t nuv; /* Number of upvalues */
VarIndex varmap[LJ_MAX_LOCVAR]; /* Map from register to variable idx. */
@ -217,7 +220,7 @@ GCstr *lj_parse_keepstr(LexState *ls, const char *str, size_t len)
/* Get next element in jump list. */
static BCPos jmp_next(FuncState *fs, BCPos pc)
{
ptrdiff_t delta = bc_j(proto_ins(fs->pt, pc));
ptrdiff_t delta = bc_j(fs->bcbase[pc].ins);
if ((BCPos)delta == NO_JMP)
return NO_JMP;
else
@ -228,7 +231,7 @@ static BCPos jmp_next(FuncState *fs, BCPos pc)
static int jmp_novalue(FuncState *fs, BCPos list)
{
for (; list != NO_JMP; list = jmp_next(fs, list)) {
BCOp op = bc_op(proto_ins(fs->pt, list >= 1 ? list-1 : list));
BCOp op = bc_op(fs->bcbase[list >= 1 ? list-1 : list].ins);
if (!(op == BC_ISTC || op == BC_ISFC)) return 1;
}
return 0;
@ -237,15 +240,15 @@ static int jmp_novalue(FuncState *fs, BCPos list)
/* Patch register of test instructions. */
static int jmp_patchtestreg(FuncState *fs, BCPos pc, BCReg reg)
{
BCIns *i = proto_insptr(fs->pt, pc >= 1 ? pc-1 : pc);
BCOp op = bc_op(*i);
BCIns *ip = &fs->bcbase[pc >= 1 ? pc-1 : pc].ins;
BCOp op = bc_op(*ip);
if (!(op == BC_ISTC || op == BC_ISFC))
return 0; /* Cannot patch other instructions. */
if (reg != NO_REG && reg != bc_d(*i)) {
setbc_a(i, reg);
if (reg != NO_REG && reg != bc_d(*ip)) {
setbc_a(ip, reg);
} else { /* Nothing to store or already in the right register. */
setbc_op(i, op+(BC_IST-BC_ISTC));
setbc_a(i, 0);
setbc_op(ip, op+(BC_IST-BC_ISTC));
setbc_a(ip, 0);
}
return 1;
}
@ -260,7 +263,7 @@ static void jmp_dropval(FuncState *fs, BCPos list)
/* Patch jump instruction to target. */
static void jmp_patchins(FuncState *fs, BCPos pc, BCPos dest)
{
BCIns *jmp = proto_insptr(fs->pt, pc);
BCIns *jmp = &fs->bcbase[pc].ins;
BCPos offset = dest-(pc+1)+BCBIAS_J;
lua_assert(dest != NO_JMP);
if (offset > BCMAX_D)
@ -355,33 +358,30 @@ static void expr_free(FuncState *fs, ExpDesc *e)
/* -- Bytecode emitter ---------------------------------------------------- */
/* Emit bytecode instruction. */
static BCPos bcemit_INS(FuncState *fs, BCIns i)
static BCPos bcemit_INS(FuncState *fs, BCIns ins)
{
GCproto *pt;
BCIns *bc;
BCLine *lineinfo;
jmp_patchval(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
BCPos pc = fs->pc;
LexState *ls = fs->ls;
jmp_patchval(fs, fs->jpc, pc, NO_REG, pc);
fs->jpc = NO_JMP;
pt = fs->pt;
bc = proto_bc(pt);
lineinfo = proto_lineinfo(pt);
if (LJ_UNLIKELY(fs->pc >= pt->sizebc)) {
checklimit(fs, fs->pc, LJ_MAX_BCINS, "bytecode instructions");
lj_mem_growvec(fs->L, bc, pt->sizebc, LJ_MAX_BCINS, BCIns);
setmref(pt->bc, bc);
lj_mem_growvec(fs->L, lineinfo, pt->sizelineinfo, LJ_MAX_BCINS, BCLine);
setmref(pt->lineinfo, lineinfo);
if (LJ_UNLIKELY(pc >= fs->bclim)) {
ptrdiff_t base = fs->bcbase - ls->bcstack;
checklimit(fs, ls->sizebcstack, LJ_MAX_BCINS, "bytecode instructions");
lj_mem_growvec(fs->L, ls->bcstack, ls->sizebcstack, LJ_MAX_BCINS,BCInsLine);
fs->bclim = (BCPos)(ls->sizebcstack - base);
fs->bcbase = ls->bcstack + base;
}
bc[fs->pc] = i;
lineinfo[fs->pc] = fs->ls->lastline;
return fs->pc++;
fs->bcbase[pc].ins = ins;
fs->bcbase[pc].line = ls->lastline;
fs->pc = pc+1;
return pc;
}
#define bcemit_ABC(fs, o, a, b, c) bcemit_INS(fs, BCINS_ABC(o, a, b, c))
#define bcemit_AD(fs, o, a, d) bcemit_INS(fs, BCINS_AD(o, a, d))
#define bcemit_AJ(fs, o, a, j) bcemit_INS(fs, BCINS_AJ(o, a, j))
#define bcptr(fs, e) (proto_insptr((fs)->pt, (e)->u.s.info))
#define bcptr(fs, e) (&(fs)->bcbase[(e)->u.s.info].ins)
/* -- Bytecode emitter for expressions ------------------------------------ */
@ -579,14 +579,12 @@ static void bcemit_method(FuncState *fs, ExpDesc *e, ExpDesc *key)
/* Emit bytecode to set a range of registers to nil. */
static void bcemit_nil(FuncState *fs, BCReg from, BCReg n)
{
BCIns *pr;
if (fs->pc > fs->lasttarget) { /* No jumps to current position? */
BCReg pfrom, pto;
pr = proto_insptr(fs->pt, fs->pc-1);
pfrom = bc_a(*pr);
switch (bc_op(*pr)) { /* Try to merge with the previous instruction. */
BCIns *ip = &fs->bcbase[fs->pc-1].ins;
BCReg pto, pfrom = bc_a(*ip);
switch (bc_op(*ip)) { /* Try to merge with the previous instruction. */
case BC_KPRI:
if (bc_d(*pr) != ~LJ_TNIL) break;
if (bc_d(*ip) != ~LJ_TNIL) break;
if (from == pfrom) {
if (n == 1) return;
} else if (from == pfrom+1) {
@ -598,10 +596,10 @@ static void bcemit_nil(FuncState *fs, BCReg from, BCReg n)
fs->pc--; /* Drop KPRI. */
break;
case BC_KNIL:
pto = bc_d(*pr);
pto = bc_d(*ip);
if (pfrom <= from && from <= pto+1) { /* Can we connect both ranges? */
if (from+n-1 > pto)
setbc_d(pr, from+n-1); /* Patch previous instruction range. */
setbc_d(ip, from+n-1); /* Patch previous instruction range. */
return;
}
break;
@ -623,8 +621,8 @@ static BCPos bcemit_jmp(FuncState *fs)
BCPos j = fs->pc - 1;
fs->jpc = NO_JMP;
if ((int32_t)j >= (int32_t)fs->lasttarget &&
bc_op(proto_ins(fs->pt, j)) == BC_UCLO)
setbc_j(proto_insptr(fs->pt, j), NO_JMP);
bc_op(fs->bcbase[j].ins) == BC_UCLO)
setbc_j(&fs->bcbase[j].ins, NO_JMP);
else
j = bcemit_AJ(fs, BC_JMP, fs->freereg, NO_JMP);
jmp_append(fs, &j, jpc);
@ -634,8 +632,8 @@ static BCPos bcemit_jmp(FuncState *fs)
/* Invert branch condition of bytecode instruction. */
static void invertcond(FuncState *fs, ExpDesc *e)
{
BCIns *i = bcptr(fs, e) - 1;
setbc_op(i, bc_op(*i)^1);
BCIns *ip = &fs->bcbase[e->u.s.info - 1].ins;
setbc_op(ip, bc_op(*ip)^1);
}
/* Emit conditional branch. */
@ -643,9 +641,9 @@ static BCPos bcemit_branch(FuncState *fs, ExpDesc *e, int cond)
{
BCPos pc;
if (e->k == VRELOCABLE) {
BCIns *i = bcptr(fs, e);
if (bc_op(*i) == BC_NOT) {
*i = BCINS_AD(cond ? BC_ISF : BC_IST, 0, bc_d(*i));
BCIns *ip = bcptr(fs, e);
if (bc_op(*ip) == BC_NOT) {
*ip = BCINS_AD(cond ? BC_ISF : BC_IST, 0, bc_d(*ip));
return bcemit_jmp(fs);
}
}
@ -1094,10 +1092,10 @@ static int bcopisret(BCOp op)
}
/* Fixup return instruction for prototype. */
static void fs_fixup_ret(FuncState *fs, GCproto *pt)
static void fs_fixup_ret(FuncState *fs)
{
BCPos lastpc = fs->pc;
if (lastpc <= fs->lasttarget || !bcopisret(bc_op(proto_ins(pt, lastpc-1)))) {
if (lastpc <= fs->lasttarget || !bcopisret(bc_op(fs->bcbase[lastpc-1].ins))) {
if (fs->flags & PROTO_HAS_FNEW)
bcemit_AJ(fs, BC_UCLO, 0, 0);
bcemit_AD(fs, BC_RET0, 0, 1); /* Need final return. */
@ -1106,16 +1104,16 @@ static void fs_fixup_ret(FuncState *fs, GCproto *pt)
if (fs->flags & PROTO_FIXUP_RETURN) {
BCPos pc;
for (pc = 0; pc < lastpc; pc++) {
BCIns i = proto_ins(pt, pc);
BCIns ins = fs->bcbase[pc].ins;
BCPos offset;
switch (bc_op(i)) {
switch (bc_op(ins)) {
case BC_CALLMT: case BC_CALLT:
case BC_RETM: case BC_RET: case BC_RET0: case BC_RET1:
offset = bcemit_INS(fs, i)-(pc+1)+BCBIAS_J; /* Copy return ins. */
offset = bcemit_INS(fs, ins)-(pc+1)+BCBIAS_J; /* Copy return ins. */
if (offset > BCMAX_D)
err_syntax(fs->ls, LJ_ERR_XFIXUP);
/* Replace with UCLO plus branch. */
*proto_insptr(pt, pc) = BCINS_AD(BC_UCLO, 0, offset);
fs->bcbase[pc].ins = BCINS_AD(BC_UCLO, 0, offset);
break;
case BC_UCLO:
return; /* We're done. */
@ -1132,26 +1130,29 @@ static GCproto *fs_finish(LexState *ls, BCLine line)
lua_State *L = ls->L;
FuncState *fs = ls->fs;
GCproto *pt = fs->pt;
BCIns *bc;
BCLine *lineinfo;
/* Apply final fixups. */
var_remove(ls, 0);
fs_fixup_ret(fs, pt);
/* Reallocate arrays. */
bc = proto_bc(pt);
lj_mem_reallocvec(L, bc, pt->sizebc, fs->pc, BCIns);
setmref(pt->bc, bc);
pt->sizebc = fs->pc;
fs_fixup_ret(fs);
fs_fixup_k(fs, pt);
fs_fixup_uv(fs, pt);
lineinfo = proto_lineinfo(pt);
lj_mem_reallocvec(L, lineinfo, pt->sizelineinfo, fs->pc, BCLine);
{
MSize i, n = fs->pc;
BCInsLine *base = fs->bcbase;
BCLine *lineinfo;
BCIns *bc = lj_mem_newvec(L, n, BCIns);
setmref(pt->bc, bc);
pt->sizebc = fs->pc;
lineinfo = lj_mem_newvec(L, n, BCLine);
setmref(pt->lineinfo, lineinfo);
pt->sizelineinfo = fs->pc;
pt->sizelineinfo = n;
for (i = 0; i < n; i++) {
bc[i] = base[i].ins;
lineinfo[i] = base[i].line;
}
}
{
MSize n = ls->vtop - fs->vbase;
@ -1173,6 +1174,7 @@ static GCproto *fs_finish(LexState *ls, BCLine line)
/* Initialize prototype fields. */
setgcref(pt->chunkname, obj2gco(ls->chunkname));
pt->flags = fs->flags;
pt->numparams = fs->numparams;
pt->framesize = fs->framesize;
pt->linedefined = fs->linedefined;
pt->lastlinedefined = line;
@ -1196,7 +1198,7 @@ static GCproto *fs_finish(LexState *ls, BCLine line)
}
/* Initialize a new FuncState. */
static void fs_init(LexState *ls, FuncState *fs, BCLine line)
static void fs_init(LexState *ls, FuncState *fs)
{
lua_State *L = ls->L;
GCproto *pt = lj_func_newproto(L);
@ -1216,7 +1218,6 @@ static void fs_init(LexState *ls, FuncState *fs, BCLine line)
fs->bl = NULL;
fs->flags = 0;
fs->framesize = 2; /* Minimum frame size. */
fs->linedefined = line;
fs->kt = lj_tab_new(L, 0, 0);
/* Anchor table of constants and prototype (to avoid being collected). */
settabV(L, L->top, fs->kt);
@ -1333,7 +1334,7 @@ static void expr_table(LexState *ls, ExpDesc *e)
BCReg kidx;
t = lj_tab_new(fs->L, 0, 0);
kidx = const_gc(fs, obj2gco(t), LJ_TTAB);
*proto_insptr(fs->pt, pc) = BCINS_AD(BC_TDUP, freg-1, kidx);
fs->bcbase[pc].ins = BCINS_AD(BC_TDUP, freg-1, kidx);
}
vcall = 0;
expr_kvalue(&k, &key);
@ -1350,14 +1351,15 @@ static void expr_table(LexState *ls, ExpDesc *e)
}
lex_match(ls, '}', '{', line);
if (vcall) {
BCIns *i = proto_insptr(fs->pt, fs->pc-1);
BCInsLine *ilp = &fs->bcbase[fs->pc-1];
ExpDesc en;
lua_assert(bc_a(*i)==freg && bc_op(*i) == (narr>256?BC_TSETV:BC_TSETB));
lua_assert(bc_a(ilp->ins) == freg &&
bc_op(ilp->ins) == (narr > 256 ? BC_TSETV : BC_TSETB));
expr_init(&en, VKNUM, 0);
setintV(&en.u.nval, narr-1);
if (narr > 256) { fs->pc--; i--; }
*i = BCINS_AD(BC_TSETM, freg, const_num(fs, &en));
setbc_b(i-1, 0);
if (narr > 256) { fs->pc--; ilp--; }
ilp->ins = BCINS_AD(BC_TSETM, freg, const_num(fs, &en));
setbc_b(&ilp[-1].ins, 0);
}
if (pc == fs->pc-1) { /* Make expr relocable if possible. */
e->u.s.info = pc;
@ -1370,15 +1372,14 @@ static void expr_table(LexState *ls, ExpDesc *e)
if (!needarr) narr = 0;
else if (narr < 3) narr = 3;
else if (narr > 0x7ff) narr = 0x7ff;
setbc_d(proto_insptr(fs->pt, pc), (uint32_t)narr|(hsize2hbits(nhash)<<11));
setbc_d(&fs->bcbase[pc].ins, (uint32_t)narr|(hsize2hbits(nhash)<<11));
}
}
/* Parse function parameters. */
static void parse_params(LexState *ls, int needself)
static BCReg parse_params(LexState *ls, int needself)
{
FuncState *fs = ls->fs;
GCproto *pt = fs->pt;
BCReg nparams = 0;
lex_check(ls, '(');
if (needself) {
@ -1399,9 +1400,9 @@ static void parse_params(LexState *ls, int needself)
} while (lex_opt(ls, ','));
}
var_add(ls, nparams);
pt->numparams = cast_byte(fs->nactvar);
bcreg_reserve(fs, fs->nactvar);
lex_check(ls, ')');
return fs->nactvar;
}
/* Forward declaration. */
@ -1410,18 +1411,23 @@ static void parse_chunk(LexState *ls);
/* Parse body of a function. */
static void parse_body(LexState *ls, ExpDesc *e, int needself, BCLine line)
{
FuncState *fs, cfs;
FuncState cfs, *fs = ls->fs;
BCReg kidx;
BCLine lastline;
GCproto *pt;
fs_init(ls, &cfs, line);
parse_params(ls, needself);
ptrdiff_t oldbase = fs->bcbase - ls->bcstack;
fs_init(ls, &cfs);
cfs.linedefined = line;
cfs.numparams = (uint8_t)parse_params(ls, needself);
cfs.bcbase = fs->bcbase + fs->pc;
cfs.bclim = fs->bclim - fs->pc;
parse_chunk(ls);
lastline = ls->linenumber;
lex_match(ls, TK_end, TK_function, line);
pt = fs_finish(ls, lastline);
fs->bcbase = ls->bcstack + oldbase; /* May have been reallocated. */
fs->bclim = ls->sizebcstack - oldbase;
/* Store new prototype in the constant array of the parent. */
fs = ls->fs;
kidx = const_gc(fs, obj2gco(pt), LJ_TPROTO);
expr_init(e, VRELOCABLE, bcemit_AD(fs, BC_FNEW, 0, kidx));
if (!(fs->flags & PROTO_HAS_FNEW)) {
@ -1485,7 +1491,7 @@ static void parse_args(LexState *ls, ExpDesc *e)
}
expr_init(e, VCALL, bcemit_INS(fs, ins));
e->u.s.aux = base;
proto_lineinfo(fs->pt)[fs->pc - 1] = line;
fs->bcbase[fs->pc - 1].line = line;
fs->freereg = base+1; /* Leave one result by default. */
}
@ -1693,7 +1699,7 @@ static void scope_begin(FuncState *fs, FuncScope *bl, int isbreakable)
{
bl->breaklist = NO_JMP;
bl->isbreakable = (uint8_t)isbreakable;
bl->nactvar = fs->nactvar;
bl->nactvar = (uint8_t)fs->nactvar;
bl->upval = 0;
bl->prev = fs->bl;
fs->bl = bl;
@ -1766,11 +1772,11 @@ static void parse_return(LexState *ls)
BCReg nret = expr_list(ls, &e);
if (nret == 1) { /* Return one result. */
if (e.k == VCALL) { /* Check for tail call. */
BCIns *i = bcptr(fs, &e);
BCIns *ip = bcptr(fs, &e);
/* It doesn't pay off to add BC_VARGT just for 'return ...'. */
if (bc_op(*i) == BC_VARG) goto notailcall;
if (bc_op(*ip) == BC_VARG) goto notailcall;
fs->pc--;
ins = BCINS_AD(bc_op(*i)-BC_CALL+BC_CALLT, bc_a(*i), bc_c(*i));
ins = BCINS_AD(bc_op(*ip)-BC_CALL+BC_CALLT, bc_a(*ip), bc_c(*ip));
} else { /* Can return the result from any register. */
ins = BCINS_AD(BC_RET1, expr_toanyreg(fs, &e), 2);
}
@ -1957,7 +1963,7 @@ static void parse_func(LexState *ls, BCLine line)
parse_body(ls, &b, needself, line);
fs = ls->fs;
bcemit_store(fs, &v, &b);
proto_lineinfo(fs->pt)[fs->pc - 1] = line; /* Set line for the store. */
fs->bcbase[fs->pc - 1].line = line; /* Set line for the store. */
}
/* -- Loop and conditional statements ------------------------------------- */
@ -2033,9 +2039,9 @@ static void parse_for_body(LexState *ls, BCReg base, BCLine line,
jmp_patchins(fs, loop, fs->pc);
bcemit_ABC(fs, BC_ITERC, base+3, nvars+1, 2+1);
loopend = bcemit_AJ(fs, BC_ITERL, base+3, NO_JMP);
proto_lineinfo(fs->pt)[loopend-1] = line;
fs->bcbase[loopend-1].line = line;
}
proto_lineinfo(fs->pt)[loopend] = line; /* Fix line for control ins. */
fs->bcbase[loopend].line = line; /* Fix line for control ins. */
jmp_patchins(fs, loopend, loop+1);
}
@ -2210,7 +2216,11 @@ GCproto *lj_parse(LexState *ls)
setstrV(L, L->top, ls->chunkname); /* Anchor chunkname string. */
incr_top(L);
ls->level = 0;
fs_init(ls, &fs, 0);
fs_init(ls, &fs);
fs.linedefined = 0;
fs.numparams = 0;
fs.bcbase = NULL;
fs.bclim = 0;
fs.flags |= PROTO_IS_VARARG; /* Main chunk is always a vararg func. */
lj_lex_next(ls); /* Read-ahead first token. */
parse_chunk(ls);