implemented jump table dispatch

- currently only enabled on supported platforms (GNU C Compiler + Clang)
- when enabled, sped up examples/fibtest.cosmo by about 20% (avg of 11.179s prior and 8.799 after)

NOTE: malicious dumps can trivially cause crashes now by having junk function chunks
This commit is contained in:
CPunch 2023-06-03 01:17:28 -05:00 committed by cpunch
parent 7bca6927a9
commit 2b3825d258
4 changed files with 647 additions and 589 deletions

View File

@ -64,7 +64,7 @@ typedef enum
OP_FALSE, OP_FALSE,
OP_NIL, OP_NIL,
OP_RETURN OP_RETURN,
} COPCODE; // there can be a max of 256 instructions } COPCODE; // there can be a max of 256 instructions
#endif #endif

View File

@ -14,7 +14,15 @@
performance, however this will produce undefined behavior as you reach the stack limit (and may performance, however this will produce undefined behavior as you reach the stack limit (and may
cause a seg fault!). It is recommended to keep this enabled. cause a seg fault!). It is recommended to keep this enabled.
*/ */
#define SAFE_STACK // #define SAFE_STACK
/*
NAN_BOXXED:
if undefined, the interpreter will use a tagged union to store values. This is the default.
Note that even though the sizeof(CValue) is 8 bytes for NAN_BOXXED (as opposed to 16 bytes for
the tagged union) no performance benefits were measured. I recommend keeping this undefined for
now.
*/
// #define NAN_BOXXED // #define NAN_BOXXED
// forward declare *most* stuff so our headers are cleaner // forward declare *most* stuff so our headers are cleaner

391
src/cvm.c
View File

@ -672,15 +672,62 @@ int _tbl__next(CState *state, int nargs, CValue *args)
return -1; \ return -1; \
} }
static inline uint8_t READBYTE(CCallFrame *frame)
{
return *frame->pc++;
}
static inline uint16_t READUINT(CCallFrame *frame)
{
frame->pc += 2;
return *(uint16_t *)(&frame->pc[-2]);
}
#ifdef VM_JUMPTABLE
# define DISPATCH goto *cosmoV_dispatchTable[READBYTE(frame)]
# define CASE(op) \
DISPATCH; \
JMP_##op
# define JMPLABEL(op) &&JMP_##op
# define SWITCH \
static void *cosmoV_dispatchTable[] = { \
JMPLABEL(OP_LOADCONST), JMPLABEL(OP_SETGLOBAL), JMPLABEL(OP_GETGLOBAL), \
JMPLABEL(OP_SETLOCAL), JMPLABEL(OP_GETLOCAL), JMPLABEL(OP_GETUPVAL), \
JMPLABEL(OP_SETUPVAL), JMPLABEL(OP_PEJMP), JMPLABEL(OP_EJMP), \
JMPLABEL(OP_JMP), JMPLABEL(OP_JMPBACK), JMPLABEL(OP_POP), \
JMPLABEL(OP_CALL), JMPLABEL(OP_CLOSURE), JMPLABEL(OP_CLOSE), \
JMPLABEL(OP_NEWTABLE), JMPLABEL(OP_NEWARRAY), JMPLABEL(OP_INDEX), \
JMPLABEL(OP_NEWINDEX), JMPLABEL(OP_NEWOBJECT), JMPLABEL(OP_SETOBJECT), \
JMPLABEL(OP_GETOBJECT), JMPLABEL(OP_GETMETHOD), JMPLABEL(OP_INVOKE), \
JMPLABEL(OP_ITER), JMPLABEL(OP_NEXT), JMPLABEL(OP_ADD), \
JMPLABEL(OP_SUB), JMPLABEL(OP_MULT), JMPLABEL(OP_DIV), \
JMPLABEL(OP_MOD), JMPLABEL(OP_POW), JMPLABEL(OP_NOT), \
JMPLABEL(OP_NEGATE), JMPLABEL(OP_COUNT), JMPLABEL(OP_CONCAT), \
JMPLABEL(OP_INCLOCAL), JMPLABEL(OP_INCGLOBAL), JMPLABEL(OP_INCUPVAL), \
JMPLABEL(OP_INCINDEX), JMPLABEL(OP_INCOBJECT), JMPLABEL(OP_EQUAL), \
JMPLABEL(OP_LESS), JMPLABEL(OP_GREATER), JMPLABEL(OP_LESS_EQUAL), \
JMPLABEL(OP_GREATER_EQUAL), JMPLABEL(OP_TRUE), JMPLABEL(OP_FALSE), \
JMPLABEL(OP_NIL), JMPLABEL(OP_RETURN), \
}; \
DISPATCH;
# define DEFAULT DISPATCH /* no-op */
#else
# define CASE(op) \
continue; \
case op
# define SWITCH switch (READBYTE(frame))
# define DEFAULT \
default: \
CERROR("unknown opcode!"); \
exit(0)
#endif
// returns -1 if panic // returns -1 if panic
int cosmoV_execute(CState *state) int cosmoV_execute(CState *state)
{ {
CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame CCallFrame *frame = &state->callFrame[state->frameCount - 1]; // grabs the current frame
CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :) CValue *constants = frame->closure->function->chunk.constants.values; // cache the pointer :)
#define READBYTE() *frame->pc++
#define READUINT() (frame->pc += 2, *(uint16_t *)(&frame->pc[-2]))
while (!state->panic) { while (!state->panic) {
#ifdef VM_DEBUG #ifdef VM_DEBUG
cosmoV_printStack(state); cosmoV_printStack(state);
@ -688,95 +735,98 @@ int cosmoV_execute(CState *state)
frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1); frame->pc - frame->closure->function->chunk.buf, state->frameCount - 1);
printf("\n"); printf("\n");
#endif #endif
switch (READBYTE()) { SWITCH
case OP_LOADCONST: { // push const[uint] to stack {
uint16_t indx = READUINT(); CASE(OP_LOADCONST) :
{ // push const[uint] to stack
uint16_t indx = READUINT(frame);
cosmoV_pushValue(state, constants[indx]); cosmoV_pushValue(state, constants[indx]);
continue;
} }
case OP_SETGLOBAL: { CASE(OP_SETGLOBAL) :
uint16_t indx = READUINT(); {
uint16_t indx = READUINT(frame);
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); CValue *val = cosmoT_insert(state, &state->globals->tbl, ident);
*val = *cosmoV_pop(state); // sets the value in the hash table *val = *cosmoV_pop(state); // sets the value in the hash table
continue;
} }
case OP_GETGLOBAL: { CASE(OP_GETGLOBAL) :
uint16_t indx = READUINT(); {
uint16_t indx = READUINT(frame);
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
CValue val; // to hold our value CValue val; // to hold our value
cosmoT_get(state, &state->globals->tbl, ident, &val); cosmoT_get(state, &state->globals->tbl, ident, &val);
cosmoV_pushValue(state, val); // pushes the value to the stack cosmoV_pushValue(state, val); // pushes the value to the stack
continue;
} }
case OP_SETLOCAL: { CASE(OP_SETLOCAL) :
uint8_t indx = READBYTE(); {
uint8_t indx = READBYTE(frame);
// set base to top of stack & pop // set base to top of stack & pop
frame->base[indx] = *cosmoV_pop(state); frame->base[indx] = *cosmoV_pop(state);
continue;
} }
case OP_GETLOCAL: { CASE(OP_GETLOCAL) :
uint8_t indx = READBYTE(); {
uint8_t indx = READBYTE(frame);
cosmoV_pushValue(state, frame->base[indx]); cosmoV_pushValue(state, frame->base[indx]);
continue; continue;
} }
case OP_GETUPVAL: { CASE(OP_GETUPVAL) :
uint8_t indx = READBYTE(); {
uint8_t indx = READBYTE(frame);
cosmoV_pushValue(state, *frame->closure->upvalues[indx]->val); cosmoV_pushValue(state, *frame->closure->upvalues[indx]->val);
continue;
} }
case OP_SETUPVAL: { CASE(OP_SETUPVAL) :
uint8_t indx = READBYTE(); {
uint8_t indx = READBYTE(frame);
*frame->closure->upvalues[indx]->val = *cosmoV_pop(state); *frame->closure->upvalues[indx]->val = *cosmoV_pop(state);
continue;
} }
case OP_PEJMP: { // pop equality jump CASE(OP_PEJMP) :
uint16_t offset = READUINT(); { // pop equality jump
uint16_t offset = READUINT(frame);
if (isFalsey(cosmoV_pop(state))) { // pop, if the condition is false, jump! if (isFalsey(cosmoV_pop(state))) { // pop, if the condition is false, jump!
frame->pc += offset; frame->pc += offset;
} }
continue;
} }
case OP_EJMP: { // equality jump CASE(OP_EJMP) :
uint16_t offset = READUINT(); { // equality jump
uint16_t offset = READUINT(frame);
if (isFalsey(cosmoV_getTop(state, 0))) { // if the condition is false, jump! if (isFalsey(cosmoV_getTop(state, 0))) { // if the condition is false, jump!
frame->pc += offset; frame->pc += offset;
} }
continue;
} }
case OP_JMP: { // jump CASE(OP_JMP) :
uint16_t offset = READUINT(); { // jump
uint16_t offset = READUINT(frame);
frame->pc += offset; frame->pc += offset;
continue;
} }
case OP_JMPBACK: { CASE(OP_JMPBACK) :
uint16_t offset = READUINT(); {
uint16_t offset = READUINT(frame);
frame->pc -= offset; frame->pc -= offset;
continue;
} }
case OP_POP: { // pops value off the stack CASE(OP_POP) :
cosmoV_setTop(state, READBYTE()); { // pops value off the stack
continue; cosmoV_setTop(state, READBYTE(frame));
} }
case OP_CALL: { CASE(OP_CALL) :
uint8_t args = READBYTE(); {
uint8_t nres = READBYTE(); uint8_t args = READBYTE(frame);
uint8_t nres = READBYTE(frame);
if (cosmoV_call(state, args, nres) != COSMOVM_OK) { if (cosmoV_call(state, args, nres) != COSMOVM_OK) {
return -1; return -1;
} }
continue;
} }
case OP_CLOSURE: { CASE(OP_CLOSURE) :
uint16_t index = READUINT(); {
uint16_t index = READUINT(frame);
CObjFunction *func = cosmoV_readFunction(constants[index]); CObjFunction *func = cosmoV_readFunction(constants[index]);
CObjClosure *closure = cosmoO_newClosure(state, func); CObjClosure *closure = cosmoO_newClosure(state, func);
cosmoV_pushRef(state, (CObj *)closure); cosmoV_pushRef(state, (CObj *)closure);
for (int i = 0; i < closure->upvalueCount; i++) { for (int i = 0; i < closure->upvalueCount; i++) {
uint8_t encoding = READBYTE(); uint8_t encoding = READBYTE(frame);
uint8_t index = READBYTE(); uint8_t index = READBYTE(frame);
if (encoding == OP_GETUPVAL) { if (encoding == OP_GETUPVAL) {
// capture upvalue from current frame's closure // capture upvalue from current frame's closure
closure->upvalues[i] = frame->closure->upvalues[index]; closure->upvalues[i] = frame->closure->upvalues[index];
@ -785,21 +835,20 @@ int cosmoV_execute(CState *state)
closure->upvalues[i] = captureUpvalue(state, frame->base + index); closure->upvalues[i] = captureUpvalue(state, frame->base + index);
} }
} }
continue;
} }
case OP_CLOSE: { CASE(OP_CLOSE) :
{
closeUpvalues(state, state->top - 1); closeUpvalues(state, state->top - 1);
cosmoV_pop(state); cosmoV_pop(state);
continue;
} }
case OP_NEWTABLE: { CASE(OP_NEWTABLE) :
uint16_t pairs = READUINT(); {
uint16_t pairs = READUINT(frame);
cosmoV_makeTable(state, pairs); cosmoV_makeTable(state, pairs);
continue;
} }
case OP_NEWARRAY: { CASE(OP_NEWARRAY) :
uint16_t pairs = READUINT(); {
uint16_t pairs = READUINT(frame);
StkPtr val; StkPtr val;
CObjTable *newObj = cosmoO_newTable(state); CObjTable *newObj = cosmoO_newTable(state);
cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table cosmoV_pushRef(state, (CObj *)newObj); // so our GC doesn't free our new table
@ -816,9 +865,9 @@ int cosmoV_execute(CState *state)
// once done, pop everything off the stack + push new table // once done, pop everything off the stack + push new table
cosmoV_setTop(state, pairs + 1); // + 1 for our table cosmoV_setTop(state, pairs + 1); // + 1 for our table
cosmoV_pushRef(state, (CObj *)newObj); cosmoV_pushRef(state, (CObj *)newObj);
continue;
} }
case OP_INDEX: { CASE(OP_INDEX) :
{
StkPtr key = cosmoV_getTop(state, 0); // key should be the top of the stack StkPtr key = cosmoV_getTop(state, 0); // key should be the top of the stack
StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table StkPtr temp = cosmoV_getTop(state, 1); // after that should be the table
@ -849,9 +898,9 @@ int cosmoV_execute(CState *state)
cosmoV_setTop(state, 2); // pops the table & the key cosmoV_setTop(state, 2); // pops the table & the key
cosmoV_pushValue(state, val); // pushes the field result cosmoV_pushValue(state, val); // pushes the field result
continue;
} }
case OP_NEWINDEX: { CASE(OP_NEWINDEX) :
{
StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack
StkPtr key = cosmoV_getTop(state, 1); StkPtr key = cosmoV_getTop(state, 1);
StkPtr temp = cosmoV_getTop(state, 2); // table is after the key StkPtr temp = cosmoV_getTop(state, 2); // table is after the key
@ -866,7 +915,8 @@ int cosmoV_execute(CState *state)
CObjObject *proto = cosmoO_grabProto(obj); CObjObject *proto = cosmoO_grabProto(obj);
if (proto != NULL) { if (proto != NULL) {
if (!cosmoO_newIndexObject(state, proto, *key, if (!cosmoO_newIndexObject(
state, proto, *key,
*value)) // if it returns false, cosmoV_error was called *value)) // if it returns false, cosmoV_error was called
return -1; return -1;
} else if (obj->type == COBJ_TABLE) { } else if (obj->type == COBJ_TABLE) {
@ -882,17 +932,17 @@ int cosmoV_execute(CState *state)
// pop everything off the stack // pop everything off the stack
cosmoV_setTop(state, 3); cosmoV_setTop(state, 3);
continue;
} }
case OP_NEWOBJECT: { CASE(OP_NEWOBJECT) :
uint16_t pairs = READUINT(); {
uint16_t pairs = READUINT(frame);
cosmoV_makeObject(state, pairs); cosmoV_makeObject(state, pairs);
continue;
} }
case OP_SETOBJECT: { CASE(OP_SETOBJECT) :
{
StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack StkPtr value = cosmoV_getTop(state, 0); // value is at the top of the stack
StkPtr temp = cosmoV_getTop(state, 1); // object is after the value StkPtr temp = cosmoV_getTop(state, 1); // object is after the value
uint16_t ident = READUINT(); // use for the key uint16_t ident = READUINT(frame); // use for the key
// sanity check // sanity check
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
@ -907,12 +957,12 @@ int cosmoV_execute(CState *state)
// pop everything off the stack // pop everything off the stack
cosmoV_setTop(state, 2); cosmoV_setTop(state, 2);
continue;
} }
case OP_GETOBJECT: { CASE(OP_GETOBJECT) :
{
CValue val; // to hold our value CValue val; // to hold our value
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
uint16_t ident = READUINT(); // use for the key uint16_t ident = READUINT(frame); // use for the key
// sanity check // sanity check
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
@ -927,15 +977,15 @@ int cosmoV_execute(CState *state)
cosmoV_setTop(state, 1); // pops the object cosmoV_setTop(state, 1); // pops the object
cosmoV_pushValue(state, val); // pushes the field result cosmoV_pushValue(state, val); // pushes the field result
continue;
} }
case OP_GETMETHOD: { CASE(OP_GETMETHOD) :
{
CValue val; // to hold our value CValue val; // to hold our value
StkPtr temp = cosmoV_getTop(state, 0); // that should be the object StkPtr temp = cosmoV_getTop(state, 0); // that should be the object
uint16_t ident = READUINT(); // use for the key uint16_t ident = READUINT(frame); // use for the key
// this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead of // this is almost identical to GETOBJECT, however cosmoV_getMethod is used instead
// just cosmoV_get // of just cosmoV_get
if (IS_REF(*temp)) { if (IS_REF(*temp)) {
if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val)) if (!cosmoV_getMethod(state, cosmoV_readRef(*temp), constants[ident], &val))
return -1; return -1;
@ -948,12 +998,12 @@ int cosmoV_execute(CState *state)
cosmoV_setTop(state, 1); // pops the object cosmoV_setTop(state, 1); // pops the object
cosmoV_pushValue(state, val); // pushes the field result cosmoV_pushValue(state, val); // pushes the field result
continue;
} }
case OP_INVOKE: { CASE(OP_INVOKE) :
uint8_t args = READBYTE(); {
uint8_t nres = READBYTE(); uint8_t args = READBYTE(frame);
uint16_t ident = READUINT(); uint8_t nres = READBYTE(frame);
uint16_t ident = READUINT(frame);
StkPtr temp = cosmoV_getTop(state, args); // grabs object from stack StkPtr temp = cosmoV_getTop(state, args); // grabs object from stack
CValue val; // to hold our value CValue val; // to hold our value
@ -969,10 +1019,9 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't get from type %s!", cosmoV_typeStr(*temp));
return -1; return -1;
} }
continue;
} }
case OP_ITER: { CASE(OP_ITER) :
{
StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table StkPtr temp = cosmoV_getTop(state, 0); // should be the object/table
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
@ -992,15 +1041,16 @@ int cosmoV_execute(CState *state)
cosmoV_pushValue(state, val); cosmoV_pushValue(state, val);
cosmoV_pushRef(state, (CObj *)obj); cosmoV_pushRef(state, (CObj *)obj);
if (cosmoV_call(state, 1, 1) != if (cosmoV_call(state, 1, 1) !=
COSMOVM_OK) // we expect 1 return value on the stack, the iterable object COSMOVM_OK) // we expect 1 return value on the stack, the iterable
// object
return -1; return -1;
StkPtr iObj = cosmoV_getTop(state, 0); StkPtr iObj = cosmoV_getTop(state, 0);
if (!IS_OBJECT(*iObj)) { if (!IS_OBJECT(*iObj)) {
cosmoV_error( cosmoV_error(state,
state, "Expected iterable object! '__iter' returned %s, expected "
"Expected iterable object! '__iter' returned %s, expected <object>!", "<object>!",
cosmoV_typeStr(*iObj)); cosmoV_typeStr(*iObj));
return -1; return -1;
} }
@ -1022,11 +1072,13 @@ int cosmoV_execute(CState *state)
CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next); CObjCFunction *tbl_next = cosmoO_newCFunction(state, _tbl__next);
cosmoV_pushRef(state, (CObj *)tbl_next); // value cosmoV_pushRef(state, (CObj *)tbl_next); // value
CObjObject *obj = cosmoV_makeObject(state, 2); // pushes the new object to the stack CObjObject *obj =
cosmoV_makeObject(state, 2); // pushes the new object to the stack
cosmoO_setUserI(obj, 0); // increment for iterator cosmoO_setUserI(obj, 0); // increment for iterator
// make our CObjMethod for OP_NEXT to call // make our CObjMethod for OP_NEXT to call
CObjMethod *method = cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj *)obj); CObjMethod *method =
cosmoO_newMethod(state, cosmoV_newRef(tbl_next), (CObj *)obj);
cosmoV_setTop(state, 2); // pops the object & the tbl cosmoV_setTop(state, 2); // pops the object & the tbl
cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT cosmoV_pushRef(state, (CObj *)method); // pushes the method for OP_NEXT
@ -1035,12 +1087,11 @@ int cosmoV_execute(CState *state)
cosmoO_typeStr(obj)); cosmoO_typeStr(obj));
return -1; return -1;
} }
continue;
} }
case OP_NEXT: { CASE(OP_NEXT) :
uint8_t nresults = READBYTE(); {
uint16_t jump = READUINT(); uint8_t nresults = READBYTE(frame);
uint16_t jump = READUINT(frame);
StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack StkPtr temp = cosmoV_getTop(state, 0); // we don't actually pop this off the stack
if (!IS_METHOD(*temp)) { if (!IS_METHOD(*temp)) {
@ -1058,25 +1109,29 @@ int cosmoV_execute(CState *state)
cosmoV_setTop(state, nresults); // pop the return values cosmoV_setTop(state, nresults); // pop the return values
frame->pc += jump; frame->pc += jump;
} }
continue;
} }
case OP_ADD: { // pop 2 values off the stack & try to add them together CASE(OP_ADD) :
{
// pop 2 values off the stack & try to add them together
NUMBEROP(cosmoV_newNumber, +); NUMBEROP(cosmoV_newNumber, +);
continue;
} }
case OP_SUB: { // pop 2 values off the stack & try to subtracts them CASE(OP_SUB) :
NUMBEROP(cosmoV_newNumber, -) {
continue; // pop 2 values off the stack & try to subtracts them
NUMBEROP(cosmoV_newNumber, -);
} }
case OP_MULT: { // pop 2 values off the stack & try to multiplies them together CASE(OP_MULT) :
NUMBEROP(cosmoV_newNumber, *) {
continue; // pop 2 values off the stack & try to multiplies them together
NUMBEROP(cosmoV_newNumber, *);
} }
case OP_DIV: { // pop 2 values off the stack & try to divides them CASE(OP_DIV) :
NUMBEROP(cosmoV_newNumber, /) {
continue; // pop 2 values off the stack & try to divides them
NUMBEROP(cosmoV_newNumber, /);
} }
case OP_MOD: { CASE(OP_MOD) :
{
StkPtr valA = cosmoV_getTop(state, 1); StkPtr valA = cosmoV_getTop(state, 1);
StkPtr valB = cosmoV_getTop(state, 0); StkPtr valB = cosmoV_getTop(state, 0);
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
@ -1088,9 +1143,9 @@ int cosmoV_execute(CState *state)
cosmoV_typeStr(*valB)); cosmoV_typeStr(*valB));
return -1; return -1;
} }
continue;
} }
case OP_POW: { CASE(OP_POW) :
{
StkPtr valA = cosmoV_getTop(state, 1); StkPtr valA = cosmoV_getTop(state, 1);
StkPtr valB = cosmoV_getTop(state, 0); StkPtr valB = cosmoV_getTop(state, 0);
if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) { if (IS_NUMBER(*valA) && IS_NUMBER(*valB)) {
@ -1102,13 +1157,13 @@ int cosmoV_execute(CState *state)
cosmoV_typeStr(*valB)); cosmoV_typeStr(*valB));
return -1; return -1;
} }
continue;
} }
case OP_NOT: { CASE(OP_NOT) :
{
cosmoV_pushBoolean(state, isFalsey(cosmoV_pop(state))); cosmoV_pushBoolean(state, isFalsey(cosmoV_pop(state)));
continue;
} }
case OP_NEGATE: { // pop 1 value off the stack & try to negate CASE(OP_NEGATE) :
{ // pop 1 value off the stack & try to negate
StkPtr val = cosmoV_getTop(state, 0); StkPtr val = cosmoV_getTop(state, 0);
if (IS_NUMBER(*val)) { if (IS_NUMBER(*val)) {
@ -1118,9 +1173,9 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
continue;
} }
case OP_COUNT: { CASE(OP_COUNT) :
{
StkPtr temp = cosmoV_getTop(state, 0); StkPtr temp = cosmoV_getTop(state, 0);
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
@ -1132,16 +1187,16 @@ int cosmoV_execute(CState *state)
cosmoV_pop(state); cosmoV_pop(state);
cosmoV_pushNumber(state, count); // pushes the count onto the stack cosmoV_pushNumber(state, count); // pushes the count onto the stack
continue;
} }
case OP_CONCAT: { CASE(OP_CONCAT) :
uint8_t vals = READBYTE(); {
uint8_t vals = READBYTE(frame);
cosmoV_concat(state, vals); cosmoV_concat(state, vals);
continue;
} }
case OP_INCLOCAL: { // this leaves the value on the stack CASE(OP_INCLOCAL) :
int8_t inc = READBYTE() - 128; // amount we're incrementing by { // this leaves the value on the stack
uint8_t indx = READBYTE(); int8_t inc = READBYTE(frame) - 128; // amount we're incrementing by
uint8_t indx = READBYTE(frame);
StkPtr val = &frame->base[indx]; StkPtr val = &frame->base[indx];
// check that it's a number value // check that it's a number value
@ -1152,12 +1207,11 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
continue;
} }
case OP_INCGLOBAL: { CASE(OP_INCGLOBAL) :
int8_t inc = READBYTE() - 128; // amount we're incrementing by {
uint16_t indx = READUINT(); int8_t inc = READBYTE(frame) - 128; // amount we're incrementing by
uint16_t indx = READUINT(frame);
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
CValue *val = cosmoT_insert(state, &state->globals->tbl, ident); CValue *val = cosmoT_insert(state, &state->globals->tbl, ident);
@ -1169,12 +1223,11 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
continue;
} }
case OP_INCUPVAL: { CASE(OP_INCUPVAL) :
int8_t inc = READBYTE() - 128; // amount we're incrementing by {
uint8_t indx = READBYTE(); int8_t inc = READBYTE(frame) - 128; // amount we're incrementing by
uint8_t indx = READBYTE(frame);
CValue *val = frame->closure->upvalues[indx]->val; CValue *val = frame->closure->upvalues[indx]->val;
// check that it's a number value // check that it's a number value
@ -1185,16 +1238,16 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val)); cosmoV_error(state, "Expected number, got %s!", cosmoV_typeStr(*val));
return -1; return -1;
} }
continue;
} }
case OP_INCINDEX: { CASE(OP_INCINDEX) :
int8_t inc = READBYTE() - 128; // amount we're incrementing by {
int8_t inc = READBYTE(frame) - 128; // amount we're incrementing by
StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key StkPtr temp = cosmoV_getTop(state, 1); // object should be above the key
StkPtr key = cosmoV_getTop(state, 0); // grabs key StkPtr key = cosmoV_getTop(state, 0); // grabs key
if (!IS_REF(*temp)) { if (!IS_REF(*temp)) {
cosmoV_error(state, "Couldn't index non-indexable type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't index non-indexable type %s!",
cosmoV_typeStr(*temp));
return -1; return -1;
} }
@ -1236,12 +1289,11 @@ int cosmoV_execute(CState *state)
cosmoV_typeStr(*temp)); cosmoV_typeStr(*temp));
return -1; return -1;
} }
continue;
} }
case OP_INCOBJECT: { CASE(OP_INCOBJECT) :
int8_t inc = READBYTE() - 128; // amount we're incrementing by {
uint16_t indx = READUINT(); int8_t inc = READBYTE(frame) - 128; // amount we're incrementing by
uint16_t indx = READUINT(frame);
StkPtr temp = cosmoV_getTop(state, 0); // object should be at the top of the stack StkPtr temp = cosmoV_getTop(state, 0); // object should be at the top of the stack
CValue ident = constants[indx]; // grabs identifier CValue ident = constants[indx]; // grabs identifier
@ -1270,57 +1322,42 @@ int cosmoV_execute(CState *state)
cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp)); cosmoV_error(state, "Couldn't set a field on type %s!", cosmoV_typeStr(*temp));
return -1; return -1;
} }
continue;
} }
case OP_EQUAL: { CASE(OP_EQUAL) :
{
// pop vals // pop vals
StkPtr valB = cosmoV_pop(state); StkPtr valB = cosmoV_pop(state);
StkPtr valA = cosmoV_pop(state); StkPtr valA = cosmoV_pop(state);
// compare & push // compare & push
cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB)); cosmoV_pushBoolean(state, cosmoV_equal(state, *valA, *valB));
continue;
} }
case OP_GREATER: { CASE(OP_GREATER) :
NUMBEROP(cosmoV_newBoolean, >) {
continue; NUMBEROP(cosmoV_newBoolean, >);
} }
case OP_LESS: { CASE(OP_LESS) :
NUMBEROP(cosmoV_newBoolean, <) {
continue; NUMBEROP(cosmoV_newBoolean, <);
} }
case OP_GREATER_EQUAL: { CASE(OP_GREATER_EQUAL) :
NUMBEROP(cosmoV_newBoolean, >=) {
continue; NUMBEROP(cosmoV_newBoolean, >=);
} }
case OP_LESS_EQUAL: { CASE(OP_LESS_EQUAL)
NUMBEROP(cosmoV_newBoolean, <=) : {NUMBEROP(cosmoV_newBoolean, <=)} CASE(OP_TRUE) : cosmoV_pushBoolean(state, true);
continue; CASE(OP_FALSE) : cosmoV_pushBoolean(state, false);
} CASE(OP_NIL) : cosmoV_pushValue(state, cosmoV_newNil());
case OP_TRUE: CASE(OP_RETURN) :
cosmoV_pushBoolean(state, true); {
continue; uint8_t res = READBYTE(frame);
case OP_FALSE:
cosmoV_pushBoolean(state, false);
continue;
case OP_NIL:
cosmoV_pushValue(state, cosmoV_newNil());
continue;
case OP_RETURN: {
uint8_t res = READBYTE();
return res; return res;
} }
default: DEFAULT;
CERROR("unknown opcode!");
exit(0);
} }
} }
#undef READBYTE // we'll only reach this if state->panic is true
#undef READUINT
// we'll only reach this is state->panic is true
return -1; return -1;
} }

View File

@ -8,6 +8,19 @@
// #define VM_DEBUG // #define VM_DEBUG
/*
if we're using GNUC or clang, we can use computed gotos which speeds up
cosmoV_execute by about 20% from benchmarking. of course, if you know
your compiler supports computed gotos, you can define VM_JUMPTABLE
BTW: be weary of maliciously crafted cosmo dumps!! it's very easy to crash
cosmo with this enabled and reading invalid opcodes due to us just using the
opcode as an index into the jump table
*/
#if defined(__GNUC__) || defined(__clang__)
# define VM_JUMPTABLE
#endif
typedef enum typedef enum
{ {
COSMOVM_OK, COSMOVM_OK,