327 lines
7.8 KiB
C
327 lines
7.8 KiB
C
#define i_implement
|
|
#include"vm.h"
|
|
#undef i_implement
|
|
|
|
#include"str.h"
|
|
|
|
#include<math.h>
|
|
|
|
size_t lvm_run(LVM *L, LFunc *func, size_t arg_count, LValue *regs) {
|
|
if(func->is_native) {
|
|
return func->native_func(L, func->ud, arg_count, regs);
|
|
}
|
|
|
|
static void *dispatch_table[] = {
|
|
[L_GETGLOBAL] = &&do_getglobal,
|
|
[L_SETGLOBAL] = &&do_setglobal,
|
|
[L_SETINT16] = &&do_setint16,
|
|
[L_SETINT32] = &&do_setint32,
|
|
[L_SETFLOAT] = &&do_setfloat,
|
|
[L_SETSTR] = &&do_setstr,
|
|
[L_SETTABLE] = &&do_settable,
|
|
[L_SETBOOL] = &&do_setbool,
|
|
[L_SETNIL] = &&do_setnil,
|
|
[L_SETFUNC] = &&do_setfunc,
|
|
[L_ADD] = &&do_add,
|
|
[L_SUB] = &&do_sub,
|
|
[L_MUL] = &&do_mul,
|
|
[L_DIV] = &&do_div,
|
|
[L_MOD] = &&do_mod,
|
|
[L_RET] = &&do_ret,
|
|
[L_JNOTCOND] = &&do_jnotcond,
|
|
[L_MOVE] = &&do_move,
|
|
[L_CALL] = &&do_call,
|
|
[L_JUMP] = &&do_jump,
|
|
[L_ADVANCETEST] = &&do_advancetest,
|
|
[L_COND_EQ] = &&do_cond_eq,
|
|
[L_COND_NEQ] = &&do_cond_neq,
|
|
[L_SETFIELD] = &&do_setfield,
|
|
[L_GETFIELD] = &&do_getfield,
|
|
};
|
|
|
|
LUnit *unit = func->unit;
|
|
|
|
LInst *inst = func->lua_instrs;
|
|
#define DISPATCH() goto *dispatch_table[(++inst)->opcode]
|
|
|
|
inst--;
|
|
DISPATCH();
|
|
|
|
do_getglobal:;
|
|
{
|
|
uint8_t *area = unit->abyss + inst->bc;
|
|
size_t len = *(uint16_t*) area;
|
|
area += 2;
|
|
|
|
LString *str = lstring_new(len, area);
|
|
regs[inst->a] = ltable_get(func->env, lvalue_from_string(str));
|
|
lstring_free(str);
|
|
}
|
|
DISPATCH();
|
|
|
|
do_setglobal:;
|
|
{
|
|
uint8_t *area = unit->abyss + inst->bc;
|
|
size_t len = *(uint16_t*) area;
|
|
area += 2;
|
|
|
|
LString *str = lstring_new(len, area);
|
|
ltable_set(func->env, lvalue_from_string(str), regs[inst->a]);
|
|
lvm_gc_add(L, lvalue_from_string(str));
|
|
}
|
|
DISPATCH();
|
|
|
|
do_setint16:;
|
|
regs[inst->a] = lvalue_from_int32((int16_t) inst->bc);
|
|
DISPATCH();
|
|
|
|
do_setint32:;
|
|
regs[inst->a] = lvalue_from_int32(*(int32_t*) &unit->abyss[inst->bc]);
|
|
DISPATCH();
|
|
|
|
do_setfloat:;
|
|
DISPATCH();
|
|
|
|
do_setstr:;
|
|
{
|
|
uint8_t *area = unit->abyss + inst->bc;
|
|
size_t len = *(uint16_t*) area;
|
|
area += 2;
|
|
|
|
regs[inst->a] = lvalue_raw(LTAG_STRING, (uintptr_t) lstring_new(len, area));
|
|
lvm_gc_add(L, regs[inst->a]);
|
|
}
|
|
DISPATCH();
|
|
|
|
do_settable:;
|
|
{
|
|
LTable *tbl = ltable_new(inst->bc);
|
|
lvm_gc_add(L, lvalue_from_table(tbl));
|
|
regs[inst->a] = lvalue_from_table(tbl);
|
|
}
|
|
DISPATCH();
|
|
|
|
do_setbool:;
|
|
regs[inst->a] = lvalue_from_bool(inst->b);
|
|
DISPATCH();
|
|
|
|
do_setnil:;
|
|
regs[inst->a] = lvalue_from_nil();
|
|
DISPATCH();
|
|
|
|
do_setfunc:;
|
|
regs[inst->a] = lvalue_from_func(&func->unit->funcs[inst->bc]);
|
|
DISPATCH();
|
|
|
|
do_add:;
|
|
{
|
|
LValue x = regs[inst->b];
|
|
LValue y = regs[inst->c];
|
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) + y.f);
|
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_double(x.f + lvalue_to_int32(y));
|
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) + lvalue_to_int32(y));
|
|
} else goto err;
|
|
}
|
|
DISPATCH();
|
|
|
|
do_sub:;
|
|
{
|
|
LValue x = regs[inst->b];
|
|
LValue y = regs[inst->c];
|
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) - y.f);
|
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_double(x.f - lvalue_to_int32(y));
|
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) - lvalue_to_int32(y));
|
|
} else goto err;
|
|
}
|
|
DISPATCH();
|
|
|
|
do_mul:;
|
|
{
|
|
LValue x = regs[inst->b];
|
|
LValue y = regs[inst->c];
|
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) * y.f);
|
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_double(x.f * lvalue_to_int32(y));
|
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) * lvalue_to_int32(y));
|
|
} else goto err;
|
|
}
|
|
DISPATCH();
|
|
|
|
do_div:;
|
|
{
|
|
LValue x = regs[inst->b];
|
|
LValue y = regs[inst->c];
|
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
|
regs[inst->a] = lvalue_from_double(lvalue_to_int32(x) / y.f);
|
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
|
regs[inst->a] = lvalue_from_double(x.f / lvalue_to_int32(y));
|
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
|
int32_t yv = lvalue_to_int32(y);
|
|
if(yv == 0) {
|
|
regs[inst->a] = lvalue_from_nil();
|
|
} else {
|
|
regs[inst->a] = lvalue_from_int32(lvalue_to_int32(x) / yv);
|
|
}
|
|
} else goto err;
|
|
}
|
|
DISPATCH();
|
|
|
|
do_mod:;
|
|
{
|
|
LValue x = regs[inst->b];
|
|
LValue y = regs[inst->c];
|
|
if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_FLOAT) {
|
|
regs[inst->a] = lvalue_from_double(fmod(fmod(lvalue_to_int32(x), y.f) + y.f, y.f));
|
|
} else if(lvalue_tag(x) == LTAG_FLOAT && lvalue_tag(y) == LTAG_I32) {
|
|
int32_t yv = lvalue_to_int32(y);
|
|
regs[inst->a] = lvalue_from_double(fmod(fmod(x.f, yv) + yv, yv));
|
|
} else if(lvalue_tag(x) == LTAG_I32 && lvalue_tag(y) == LTAG_I32) {
|
|
int32_t yv = lvalue_to_int32(y);
|
|
if(yv == 0) {
|
|
goto err;
|
|
} else {
|
|
regs[inst->a] = lvalue_from_int32((lvalue_to_int32(x) % yv + yv) % yv);
|
|
}
|
|
} else goto err;
|
|
}
|
|
DISPATCH();
|
|
|
|
do_jump:;
|
|
inst += (int16_t) inst->bc;
|
|
DISPATCH();
|
|
|
|
do_jnotcond:;
|
|
{
|
|
LValue v = regs[inst->a];
|
|
if(v.u == LTAG_NIL || v.u == LTAG_FALSE) {
|
|
inst += (int16_t) inst->bc;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
|
|
do_call:;
|
|
{
|
|
if(lvalue_tag(regs[inst->a]) != LTAG_FUNCTION) {
|
|
goto err;
|
|
}
|
|
|
|
uint8_t *abyss_data = unit->abyss + inst->bc;
|
|
|
|
uint8_t ret_vreg = abyss_data[0];
|
|
uint8_t arg_count = abyss_data[1];
|
|
uint8_t *args = &abyss_data[2];
|
|
|
|
LValue regs2[256];
|
|
lvm_reset_regs(regs2);
|
|
for(int i = 0; i < arg_count; i++) {
|
|
regs2[i] = regs[args[i]];
|
|
}
|
|
size_t returned_count = lvm_run(L, (LFunc*) (regs[inst->a].u & ~LTAG_MASK), arg_count, regs2);
|
|
|
|
if(returned_count) {
|
|
// TODO: more than 1 return
|
|
regs[ret_vreg] = regs2[0];
|
|
}
|
|
}
|
|
DISPATCH();
|
|
|
|
do_move:;
|
|
regs[inst->a] = regs[inst->b];
|
|
DISPATCH();
|
|
|
|
do_advancetest:;
|
|
{
|
|
int64_t a = lvalue_to_int32(regs[inst->a]);
|
|
int64_t b = lvalue_to_int32(regs[inst->b]);
|
|
int64_t c = lvalue_to_int32(regs[inst->c]);
|
|
if(!((c >= 0 && a > b) || (c < 0 && a < b))) {
|
|
inst++;
|
|
}
|
|
}
|
|
DISPATCH();
|
|
|
|
do_cond_eq:;
|
|
regs[inst->a] = lvalue_from_bool(lvalue_eq(regs[inst->b], regs[inst->c]));
|
|
DISPATCH();
|
|
|
|
do_cond_neq:;
|
|
regs[inst->a] = lvalue_from_bool(!lvalue_eq(regs[inst->b], regs[inst->c]));
|
|
DISPATCH();
|
|
|
|
do_setfield:;
|
|
{
|
|
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
|
goto err;
|
|
}
|
|
|
|
if(lvalue_tag(regs[inst->b]) == LTAG_NIL) {
|
|
goto err;
|
|
}
|
|
|
|
LTable *tbl = (void*) (regs[inst->a].u & ~LTAG_MASK);
|
|
|
|
ltable_set(tbl, regs[inst->b], regs[inst->c]);
|
|
}
|
|
DISPATCH();
|
|
|
|
do_getfield:;
|
|
{
|
|
if(lvalue_tag(regs[inst->a]) != LTAG_TABLE) {
|
|
goto err;
|
|
}
|
|
|
|
LTable *tbl = (void*) (regs[inst->b].u & ~LTAG_MASK);
|
|
|
|
regs[inst->a] = ltable_get(tbl, regs[inst->c]);
|
|
}
|
|
DISPATCH();
|
|
|
|
err:;
|
|
do_ret:;
|
|
return 0;
|
|
|
|
}
|
|
|
|
void lvm_gc_add(LVM *L, LValue lvalue) {
|
|
set_LValueU_insert(&L->gc_objects, lvalue.u);
|
|
}
|
|
|
|
LFunc *lvm_func_from_native(LFuncCallback cb, void *ud) {
|
|
LFunc *f = calloc(1, sizeof(*f));
|
|
f->is_native = true;
|
|
f->ud = ud;
|
|
f->native_func = cb;
|
|
return f;
|
|
}
|
|
|
|
bool lvalue_eq(LValue a, LValue b) {
|
|
if(a.u == b.u) {
|
|
return true;
|
|
}
|
|
|
|
if(lvalue_tag(a) == LTAG_I32 && lvalue_tag(b) == LTAG_FLOAT) {
|
|
return (a.u & ~LTAG_MASK) == b.f;
|
|
} else if(lvalue_tag(a) == LTAG_FLOAT && lvalue_tag(b) == LTAG_I32) {
|
|
return (b.u & ~LTAG_MASK) == a.f;
|
|
} else if(lvalue_tag(a) == LTAG_STRING && lvalue_tag(b) == LTAG_STRING) {
|
|
LString *sa = (LString*) (a.u & ~LTAG_MASK);
|
|
LString *sb = (LString*) (b.u & ~LTAG_MASK);
|
|
|
|
if(sa->length != sb->length) {
|
|
return false;
|
|
}
|
|
|
|
return !memcmp(sa->data, sb->data, sa->length);
|
|
}
|
|
|
|
return false;
|
|
}
|