new implementation for the Virtual Machine

This commit is contained in:
Roberto Ierusalimschy
2001-06-05 15:17:01 -03:00
parent 572a69b6af
commit 762d059a13
15 changed files with 1696 additions and 1538 deletions

416
ldebug.c
View File

@@ -1,5 +1,5 @@
/*
** $Id: ldebug.c,v 1.75 2001/03/26 14:31:49 roberto Exp roberto $
** $Id: ldebug.c,v 1.76 2001/04/06 18:25:00 roberto Exp roberto $
** Debug Interface
** See Copyright Notice in lua.h
*/
@@ -22,6 +22,7 @@
#include "ltable.h"
#include "ltm.h"
#include "luadebug.h"
#include "lvm.h"
@@ -298,6 +299,10 @@ LUA_API int lua_getinfo (lua_State *L, const l_char *what, lua_Debug *ar) {
#define check(x) if (!(x)) return 0;
#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode)
#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize)
static int checklineinfo (const Proto *pt) {
int *lineinfo = pt->lineinfo;
@@ -318,231 +323,199 @@ static int precheck (const Proto *pt) {
}
/* value for non-initialized entries in array stacklevel */
#define SL_EMPTY 255
#define checkjump(pt,sl,top,pc) if (!checkjump_aux(pt,sl,top,pc)) return 0;
static int checkjump_aux (const Proto *pt, lu_byte *sl, int top, int pc) {
check(0 <= pc && pc < pt->sizecode);
if (sl == NULL) return 1; /* not full checking */
if (sl[pc] == SL_EMPTY)
sl[pc] = (lu_byte)top;
else
check(sl[pc] == top);
return 1;
static int checkopenop (Instruction i) {
OpCode op = GET_OPCODE(i);
switch (op) {
case OP_CALL:
case OP_RETURN: {
check(GETARG_B(i) == NO_REG);
return 1;
}
case OP_SETLISTO: return 1;
default: return 0; /* invalid instruction after an open call */
}
}
static Instruction luaG_symbexec (lua_State *L, const Proto *pt,
int lastpc, int stackpos) {
int stack[MAXSTACK]; /* stores last instruction that changed a stack entry */
lu_byte *sl = NULL;
int top;
static Instruction luaG_symbexec (const Proto *pt, int lastpc, int reg) {
int pc;
if (stackpos < 0) { /* full check? */
int i;
sl = luaO_openspace(L, pt->sizecode, lu_byte);
for (i=0; i<pt->sizecode; i++) /* initialize stack-level array */
sl[i] = SL_EMPTY;
int last; /* stores position of last instruction that changed `reg' */
last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
if (reg == NO_REG) /* full check? */
check(precheck(pt));
}
top = pt->numparams;
pc = 0;
if (pt->is_vararg) /* varargs? */
top++; /* `arg' */
if (sl) sl[0] = (lu_byte)top;
while (pc < lastpc) {
const Instruction i = pt->code[pc++];
for (pc = 0; pc < lastpc; pc++) {
const Instruction i = pt->code[pc];
OpCode op = GET_OPCODE(i);
int arg1 = 0;
int arg2 = 0;
int push, pop;
check(op < NUM_OPCODES);
push = (int)luaK_opproperties[op].push;
pop = (int)luaK_opproperties[op].pop;
switch ((enum Mode)luaK_opproperties[op].mode) {
case iO: break;
case iU: arg1 = GETARG_U(i); check(arg1 >= 0); break;
case iS: arg1 = GETARG_S(i); break;
case iAB:
arg1 = GETARG_A(i); arg2 = GETARG_B(i); check(arg1 >= 0); break;
}
switch (op) {
case OP_RETURN: {
check(arg1 <= top);
pop = top-arg1;
break;
}
case OP_CALL: {
if (arg2 == MULT_RET) arg2 = 1;
check(arg1 < top);
pop = top-arg1;
push = arg2;
break;
}
case OP_PUSHNIL: {
check(arg1 > 0);
push = arg1;
break;
}
case OP_POP: {
pop = arg1;
break;
}
case OP_PUSHSTRING:
case OP_GETGLOBAL:
case OP_GETDOTTED:
case OP_PUSHSELF:
case OP_SETGLOBAL: {
check(arg1 < pt->sizekstr);
break;
}
case OP_PUSHNUM:
case OP_PUSHNEGNUM: {
check(arg1 < pt->sizeknum);
break;
}
case OP_PUSHUPVALUE: {
check(arg1 < pt->nupvalues);
break;
}
case OP_GETLOCAL:
case OP_GETINDEXED:
case OP_SETLOCAL: {
check(arg1 < top);
break;
}
case OP_SETTABLE: {
check(3 <= arg1 && arg1 <= top);
pop = arg2;
break;
}
case OP_SETLIST: {
check(arg2 >= 0);
pop = top-arg2-1;
break;
}
case OP_SETMAP: {
check(arg1 >= 0);
pop = top-arg1-1;
break;
}
case OP_CONCAT: {
pop = arg1;
break;
}
case OP_CLOSURE: {
check(arg1 < pt->sizekproto);
check(arg2 == pt->kproto[arg1]->nupvalues);
pop = arg2;
break;
}
case OP_JMPNE:
case OP_JMPEQ:
case OP_JMPLT:
case OP_JMPLE:
case OP_JMPGT:
case OP_JMPGE:
case OP_JMPT:
case OP_JMPF:
case OP_JMP: {
checkjump(pt, sl, top-pop, pc+arg1);
break;
}
case OP_FORLOOP:
case OP_LFORLOOP:
case OP_JMPONT:
case OP_JMPONF: {
int newpc = pc+arg1;
checkjump(pt, sl, top, newpc);
/* jump is forward and do not skip `lastpc' and not full check? */
if (pc < newpc && newpc <= lastpc && stackpos >= 0) {
stack[top-1] = pc-1; /* value comes from `and'/`or' */
pc = newpc; /* do the jump */
pop = 0; /* do not pop */
int a = GETARG_A(i);
int b = 0;
int c = 0;
#undef check
#define check(x) if (!(x)) { \
printf(">>>%d %d %d %d %d %d\n", op, a, b, c, pt->maxstacksize, pt->sizek); \
return 0; }
switch (getOpMode(op)) {
case iABC: {
b = GETARG_B(i);
c = GETARG_C(i);
if (testOpMode(op, OpModeBreg)) {
checkreg(pt, b);
check(c < pt->maxstacksize ||
(c >= MAXSTACK && c-MAXSTACK < pt->sizek));
}
break;
}
case OP_PUSHNILJMP: {
check(GET_OPCODE(pt->code[pc]) == OP_PUSHINT); /* only valid sequence */
case iABc: {
b = GETARG_Bc(i);
if (testOpMode(op, OpModeK)) check(b < pt->sizek);
break;
}
case OP_FORPREP: {
int endfor = pc-arg1-1; /* jump is `negative' here */
check(top >= 3);
checkjump(pt, sl, top+push, endfor);
check(GET_OPCODE(pt->code[endfor]) == OP_FORLOOP);
check(GETARG_S(pt->code[endfor]) == arg1);
break;
}
case OP_LFORPREP: {
int endfor = pc-arg1-1; /* jump is `negative' here */
check(top >= 1);
checkjump(pt, sl, top+push, endfor);
check(GET_OPCODE(pt->code[endfor]) == OP_LFORLOOP);
check(GETARG_S(pt->code[endfor]) == arg1);
break;
}
case OP_PUSHINT:
case OP_GETTABLE:
case OP_CREATETABLE:
case OP_ADD:
case OP_ADDI:
case OP_SUB:
case OP_MULT:
case OP_DIV:
case OP_POW:
case OP_MINUS:
case OP_NOT: {
case iAsBc: {
b = GETARG_sBc(i);
break;
}
}
top -= pop;
check(0 <= top && top+push <= pt->maxstacksize);
while (push--) stack[top++] = pc-1;
checkjump(pt, sl, top, pc);
if (testOpMode(op, OpModeAreg)) checkreg(pt, a);
if (testOpMode(op, OpModesetA)) {
if (a == reg) last = pc; /* change register `a' */
}
if (testOpMode(op, OpModeT))
check(GET_OPCODE(pt->code[pc+1]) == OP_CJMP);
switch (op) {
case OP_LOADNIL: {
if (a <= reg && reg <= b)
last = pc; /* set registers from `a' to `b' */
break;
}
case OP_LOADUPVAL: {
check(b < pt->nupvalues);
break;
}
case OP_GETGLOBAL:
case OP_SETGLOBAL: {
check(ttype(&pt->k[b]) == LUA_TSTRING);
break;
}
case OP_SELF: {
checkreg(pt, a+1);
if (reg == a+1) last = pc;
break;
}
case OP_CONCAT: {
check(b < c); /* at least two operands */
break;
}
case OP_JMP:
case OP_CJMP: {
int dest = pc+1+b;
check(0 <= dest && dest < pt->sizecode);
/* not full check and jump is forward and do not skip `lastpc'? */
if (reg != NO_REG && pc < dest && dest <= lastpc)
pc += b; /* do the jump */
break;
}
case OP_TESTT:
case OP_TESTF: {
if (a != NO_REG)
checkreg(pt, a);
break;
}
case OP_NILJMP: {
check(pc+2 < pt->sizecode); /* check its jump */
break;
}
case OP_CALL: {
if (b == NO_REG) b = pt->maxstacksize;
if (c == NO_REG) {
check(checkopenop(pt->code[pc+1]));
c = 1;
}
check(b > a);
checkreg(pt, b-1);
checkreg(pt, a+c-1);
if (reg >= a) last = pc; /* affect all registers above base */
break;
}
case OP_RETURN: {
if (b == NO_REG) b = pt->maxstacksize;
checkreg(pt, b-1);
break;
}
case OP_FORPREP:
case OP_TFORPREP: {
int dest = pc-b; /* jump is negated here */
check(0 <= dest && dest < pt->sizecode &&
GET_OPCODE(pt->code[dest]) == op+1);
break;
}
case OP_FORLOOP:
case OP_TFORLOOP: {
int dest = pc+b;
check(0 <= dest && dest < pt->sizecode &&
pt->code[dest] == SET_OPCODE(i, op-1));
checkreg(pt, a + ((op == OP_FORLOOP) ? 2 : 3));
break;
}
case OP_SETLIST: {
checkreg(pt, a + (b&(LFIELDS_PER_FLUSH-1)) + 1);
break;
}
case OP_CLOSURE: {
check(b < pt->sizekproto);
checkreg(pt, a + pt->kproto[b]->nupvalues - 1);
break;
}
default: break;
}
}
return (stackpos >= 0) ? pt->code[stack[stackpos]] : 1;
return pt->code[last];
}
/* }====================================================== */
int luaG_checkcode (lua_State *L, const Proto *pt) {
return luaG_symbexec(L, pt, pt->sizecode-1, -1);
int luaG_checkcode (const Proto *pt) {
return luaG_symbexec(pt, pt->sizecode, NO_REG);
}
static const l_char *getobjname (lua_State *L, StkId obj, const l_char **name) {
CallInfo *ci = ci_stack(L, obj);
if (!isLmark(ci))
return NULL; /* not an active Lua function */
else {
if (isLmark(ci)) { /* an active Lua function? */
Proto *p = ci_func(ci)->f.l;
int pc = currentpc(ci);
int stackpos = obj - ci->base;
Instruction i = luaG_symbexec(L, p, pc, stackpos);
Instruction i;
*name = luaF_getlocalname(p, stackpos+1, pc);
if (*name) /* is a local? */
return l_s("local");
i = luaG_symbexec(p, pc, stackpos); /* try symbolic execution */
lua_assert(pc != -1);
switch (GET_OPCODE(i)) {
case OP_GETGLOBAL: {
*name = getstr(p->kstr[GETARG_U(i)]);
lua_assert(ttype(&p->k[GETARG_Bc(i)]) == LUA_TSTRING);
*name = getstr(tsvalue(&p->k[GETARG_Bc(i)]));
return l_s("global");
}
case OP_GETLOCAL: {
*name = luaF_getlocalname(p, GETARG_U(i)+1, pc);
lua_assert(*name);
return l_s("local");
case OP_MOVE: {
int a = GETARG_A(i);
int b = GETARG_B(i); /* move from `b' to `a' */
if (b < a)
return getobjname(L, ci->base+b, name); /* get name for `b' */
break;
}
case OP_PUSHSELF:
case OP_GETDOTTED: {
*name = getstr(p->kstr[GETARG_U(i)]);
return l_s("field");
case OP_GETTABLE:
case OP_SELF: {
int c = GETARG_C(i) - MAXSTACK;
if (c >= 0 && ttype(&p->k[c]) == LUA_TSTRING) {
*name = getstr(tsvalue(&p->k[c]));
return l_s("field");
}
break;
}
default:
return NULL; /* no useful name found */
default: break;
}
}
return NULL; /* no useful name found */
}
@@ -576,10 +549,18 @@ void luaG_typeerror (lua_State *L, StkId o, const l_char *op) {
}
void luaG_binerror (lua_State *L, StkId p1, int t, const l_char *op) {
if (ttype(p1) == t) p1++;
lua_assert(ttype(p1) != t);
luaG_typeerror(L, p1, op);
void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
if (ttype(p1) == LUA_TSTRING) p1 = p2;
lua_assert(ttype(p1) != LUA_TSTRING);
luaG_typeerror(L, p1, l_s("concat"));
}
void luaG_aritherror (lua_State *L, StkId p1, TObject *p2) {
TObject temp;
if (luaV_tonumber(p1, &temp) != NULL)
p1 = p2; /* first operand is OK; error is in the second */
luaG_typeerror(L, p1, l_s("perform arithmetic on"));
}
@@ -592,3 +573,52 @@ void luaG_ordererror (lua_State *L, const TObject *p1, const TObject *p2) {
luaO_verror(L, l_s("attempt to compare %.10s with %.10s"), t1, t2);
}
#define opmode(t,a,b,c,sa,k,m) (((t)<<OpModeT) | \
((a)<<OpModeAreg) | ((b)<<OpModeBreg) | ((c)<<OpModeCreg) | \
((sa)<<OpModesetA) | ((k)<<OpModeK) | (m))
const unsigned char luaG_opmodes[] = {
/* T A B C sA K mode opcode */
opmode(0,1,1,0, 1,0,iABC), /* OP_MOVE */
opmode(0,1,0,0, 1,1,iABc), /* OP_LOADK */
opmode(0,1,0,0, 1,0,iAsBc), /* OP_LOADINT */
opmode(0,1,1,0, 1,0,iABC), /* OP_LOADNIL */
opmode(0,1,0,0, 1,0,iABc), /* OP_LOADUPVAL */
opmode(0,1,0,0, 1,1,iABc), /* OP_GETGLOBAL */
opmode(0,1,1,1, 1,0,iABC), /* OP_GETTABLE */
opmode(0,1,0,0, 0,1,iABc), /* OP_SETGLOBAL */
opmode(0,1,1,1, 0,0,iABC), /* OP_SETTABLE */
opmode(0,1,0,0, 1,0,iABc), /* OP_NEWTABLE */
opmode(0,1,1,1, 1,0,iABC), /* OP_SELF */
opmode(0,1,1,1, 1,0,iABC), /* OP_ADD */
opmode(0,1,1,1, 1,0,iABC), /* OP_SUB */
opmode(0,1,1,1, 1,0,iABC), /* OP_MUL */
opmode(0,1,1,1, 1,0,iABC), /* OP_DIV */
opmode(0,1,1,1, 1,0,iABC), /* OP_POW */
opmode(0,1,1,0, 1,0,iABC), /* OP_UNM */
opmode(0,1,1,0, 1,0,iABC), /* OP_NOT */
opmode(0,1,1,1, 1,0,iABC), /* OP_CONCAT */
opmode(0,0,0,0, 0,0,iAsBc), /* OP_JMP */
opmode(0,0,0,0, 0,0,iAsBc), /* OP_CJMP */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTEQ */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTNE */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTLT */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTLE */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTGT */
opmode(1,0,1,1, 0,0,iABC), /* OP_TESTGE */
opmode(1,0,1,0, 1,0,iABC), /* OP_TESTT */
opmode(1,0,1,0, 1,0,iABC), /* OP_TESTF */
opmode(0,1,0,0, 1,0,iAsBc), /* OP_NILJMP */
opmode(0,1,0,0, 0,0,iABC), /* OP_CALL */
opmode(0,1,0,0, 0,0,iABC), /* OP_RETURN */
opmode(0,1,0,0, 0,0,iAsBc), /* OP_FORPREP */
opmode(0,1,0,0, 0,0,iAsBc), /* OP_FORLOOP */
opmode(0,1,0,0, 0,0,iAsBc), /* OP_TFORPREP */
opmode(0,1,0,0, 0,0,iAsBc), /* OP_TFORLOOP */
opmode(0,1,0,0, 0,0,iABc), /* OP_SETLIST */
opmode(0,1,0,0, 0,0,iABc), /* OP_SETLIST0 */
opmode(0,1,0,0, 0,0,iABc) /* OP_CLOSURE */
};