minor refactoring, OP_INVOKE performance boost

This commit is contained in:
CPunch 2020-11-13 17:39:47 -06:00
parent 71d3a8e1c4
commit 9ebae876f6
10 changed files with 86 additions and 71 deletions

View File

@ -1,15 +1,29 @@
class test
function __init(self, x)
self.x = x
self.setArg(x)
end
function print(self)
print(self.x)
function fact(self)
var total = 1
return self.x
for (var i = self.x; i > 0; i = i - 1) do
total = total * i;
end
return total
end
function setArg(self, x)
self.x = x
end
end
var t = test("hello")
var t = test(1)
print(t.print())
for (var x = 1; x < 1000; x = x + 1) do
for (var i = 1; i < 100; i = i + 1) do
t.setArg(i)
print(t.fact())
end
end

View File

@ -32,6 +32,7 @@ CValue cosmoB_dsetMeta(CState *state, int nargs, CValue *args) {
return cosmoV_newNil(); // nothing
}
CValue cosmoB_dgetMeta(CState *state, int nargs, CValue *args) {
if (nargs != 1) {
cosmoV_error(state, "Expected 1 parameter, got %d!", nargs);

View File

@ -114,6 +114,8 @@ int disasmInstr(CChunk *chunk, int offset, int indent) {
return simpleInstruction("OP_GETOBJECT", offset);
case OP_SETOBJECT:
return simpleInstruction("OP_SETOBJECT", offset);
case OP_INVOKE:
return shortOperandInstruction("OP_INVOKE", chunk, offset);
case OP_ADD:
return simpleInstruction("OP_ADD", offset);
case OP_SUB:

View File

@ -95,7 +95,8 @@ bool cosmoO_equal(CObj* obj1, CObj* obj2) {
CObjObject *cosmoO_newObject(CState *state) {
CObjObject *obj = (CObjObject*)cosmoO_allocateBase(state, sizeof(CObjObject), COBJ_OBJECT);
obj->meta = state->metaObj;
cosmoV_pushValue(state, cosmoV_newObj(obj)); // so out GC can keep track of it
obj->user = NULL; // reserved for C API
cosmoV_pushValue(state, cosmoV_newObj(obj)); // so our GC can keep track of it
cosmoT_initTable(state, &obj->tbl, ARRAY_START);
cosmoV_pop(state);

View File

@ -13,8 +13,8 @@ typedef enum {
COBJ_OBJECT,
COBJ_FUNCTION,
COBJ_CFUNCTION,
COBJ_METHOD,
// internal use
COBJ_METHOD,
COBJ_CLOSURE,
COBJ_UPVALUE,
} CObjType;
@ -39,6 +39,7 @@ typedef struct CObjString {
typedef struct CObjObject {
CommonHeader; // "is a" CObj
CTable tbl;
void *user; // userdata (NULL by default)
struct CObjObject *meta; // metaobject, describes the behavior of the object
} CObjObject;

View File

@ -32,7 +32,7 @@ typedef enum {
OP_NEWOBJECT,
OP_GETOBJECT,
OP_SETOBJECT,
OP_NEWCLASS,
OP_INVOKE,
// ARITHMETIC
OP_ADD,

View File

@ -153,8 +153,6 @@ static void advance(CParseState *pstate) {
pstate->previous = pstate->current;
pstate->current = cosmoL_scanToken(pstate->lex);
//printf("got %d [%.*s]\n", pstate->current.type, pstate->current.length, pstate->current.start);
if (pstate->current.type == TOKEN_ERROR) {
// go ahead and consume the rest of the errors so it doesn't cascade
CToken temp;
@ -586,6 +584,11 @@ static void dot(CParseState *pstate, bool canAssign) {
expression(pstate);
writeu8(pstate, OP_SETOBJECT);
valuePopped(pstate, 2); // pops key, value & object
} else if (match(pstate, TOKEN_LEFT_PAREN)) { // it's an invoked call
uint8_t args = parseArguments(pstate);
writeu8(pstate, OP_INVOKE);
writeu8(pstate, args);
valuePopped(pstate, args); // pops the function & the object but pushes a result
} else {
writeu8(pstate, OP_GETOBJECT);
// pops key & object but also pushes the field so total popped is 1
@ -815,22 +818,7 @@ static void varDeclaration(CParseState *pstate, bool forceLocal) {
expression(pstate);
} while (match(pstate, TOKEN_COMMA));
if (pstate->compiler->pushedValues < pstate->compiler->savedPushed) {
writeu8(pstate, OP_NIL); // didn't get expected result
valuePushed(pstate, 1);
}
valuePushed(pstate, 1);
} else if (match(pstate, TOKEN_COMMA)) {
valuePopped(pstate, 1); // we are expecting a value
varDeclaration(pstate, forceLocal);
if (pstate->compiler->pushedValues < pstate->compiler->savedPushed) {
writeu8(pstate, OP_NIL); // didn't get expected result
valuePushed(pstate, 1);
}
valuePushed(pstate, 1); // we already popped, & when we call defineVariable it'll pop, so go ahead and fix it here
} else {
writeu8(pstate, OP_NIL);
valuePushed(pstate, 1);

View File

@ -7,8 +7,7 @@ typedef enum {
COSMO_TNIL,
COSMO_TBOOLEAN,
COSMO_TNUMBER,
COSMO_TOBJ,
COSMO_TUSERDATA
COSMO_TOBJ
} CosmoType;
typedef double cosmo_Number;
@ -21,7 +20,6 @@ typedef struct CValue {
union {
cosmo_Number num;
bool b; // boolean
void *ptr; // userdata
CObj *obj;
} val;
} CValue;

View File

@ -87,10 +87,11 @@ void pushCallFrame(CState *state, CObjClosure *closure, int args) {
frame->closure = closure;
}
void popCallFrame(CState *state) {
// offset is the offset of the callframe base we set the state->top back too (useful for passing values in the stack as arguments, like methods)
void popCallFrame(CState *state, int offset) {
closeUpvalues(state, state->callFrame[state->frameCount - 1].base); // close any upvalue still open
state->top = state->callFrame[state->frameCount - 1].base; // resets the stack
state->top = state->callFrame[state->frameCount - 1].base + offset; // resets the stack
state->frameCount--;
}
@ -112,7 +113,18 @@ typedef enum {
CALL_CFUNCTION
} preCallResult;
bool call(CState *state, CObjClosure *closure, int args) {
static inline void callCFunction(CState *state, CosmoCFunction cfunc, int args, int offset) {
StkPtr savedBase = cosmoV_getTop(state, args);
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
CValue res = cfunc(state, args, savedBase + 1);
cosmoM_unfreezeGC(state);
state->top = savedBase + offset;
cosmoV_pushValue(state, res);
}
bool call(CState *state, CObjClosure *closure, int args, int offset) {
// missmatched args, thats an obvious user error, so error.
if (args != closure->function->args) {
cosmoV_error(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args);
@ -130,7 +142,7 @@ bool call(CState *state, CObjClosure *closure, int args) {
CValue* result = cosmoV_getTop(state, 0);
// pop the callframe and return result :)
popCallFrame(state);
popCallFrame(state, offset);
// push the return value back onto the stack
cosmoV_pushValue(state, *result);
@ -139,36 +151,10 @@ bool call(CState *state, CObjClosure *closure, int args) {
bool callMethod(CState* state, CObjMethod *method, int args) {
if (method->isCFunc) {
StkPtr savedBase = state->top - args - 1;
cosmoM_freezeGC(state);
CValue res = method->cfunc->cfunc(state, args+1, savedBase);
cosmoM_unfreezeGC(state);
state->top = savedBase;
cosmoV_pushValue(state, res);
callCFunction(state, method->cfunc->cfunc, args+1, 1);
} else {
CObjClosure *closure = method->closure;
StkPtr val = state->top - args - 1;
if (args+1 != closure->function->args) {
cosmoV_error(state, "Expected %d parameters for %s, got %d!", closure->function->args, closure->function->name == NULL ? UNNAMEDCHUNK : closure->function->name->str, args+1);
return false;
}
// load function into callframe
pushCallFrame(state, closure, closure->function->args);
// execute
if (!cosmoV_execute(state))
return false;
CValue* result = state->top - 1;
// pop the callframe and return result
popCallFrame(state);
state->top++;
cosmoV_pushValue(state, *result); // and push return value
call(state, closure, args+1, 1); // offset = 1 so our stack is properly reset
}
return true;
@ -186,7 +172,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) {
switch (val->val.obj->type) {
case COBJ_CLOSURE: {
CObjClosure *closure = (CObjClosure*)(val->val.obj);
if (!call(state, closure, args)) {
if (!call(state, closure, args, 0)) {
return COSMOVM_RUNTIME_ERR;
}
break;
@ -223,14 +209,7 @@ COSMOVMRESULT cosmoV_call(CState *state, int args) {
case COBJ_CFUNCTION: {
// it's a C function, so call it
CosmoCFunction cfunc = ((CObjCFunction*)(val->val.obj))->cfunc;
CValue *savedBase = state->top - args;
cosmoM_freezeGC(state); // we don't want a GC event during c api because we don't actually trust the user to know how to evade the GC
CValue res = cfunc(state, args, savedBase);
cosmoM_unfreezeGC(state);
state->top = savedBase - 1;
cosmoV_pushValue(state, res);
callCFunction(state, cfunc, args, 0);
break;
}
default:
@ -446,6 +425,37 @@ bool cosmoV_execute(CState *state) {
cosmoV_setTop(state, 3);
break;
}
case OP_INVOKE: {
uint8_t args = READBYTE();
StkPtr key = cosmoV_getTop(state, args); // grabs key from stack
StkPtr temp = cosmoV_getTop(state, args+1); // grabs object from stack
// sanity check
if (!(temp->type == COSMO_TOBJ) || !(temp->val.obj->type == COBJ_OBJECT)) {
cosmoV_error(state, "Couldn't get from non-object!");
break;
}
CObjObject *object = (CObjObject*)temp->val.obj;
CValue val; // to hold our value
cosmoO_getObject(state, object, *key, &val);
// now set the key stack location to the object, and call it!
*key = *temp;
if (IS_CLOSURE(val)) {
call(state, cosmoV_readClosure(val), args+1, 1);
} else if (IS_CFUNCTION(val)) {
callCFunction(state, cosmoV_readCFunction(val), args+1, 1);
} else {
cosmoV_error(state, "Cannot call non-function value! got %d", val.val.obj->type);
}
// moves return value & resets stack (key now points to the stack location of our return value)
*temp = *key;
state->top = key;
break;
}
case OP_ADD: { // pop 2 values off the stack & try to add them together
BINARYOP(cosmoV_newNumber, +);
break;

View File

@ -15,7 +15,7 @@ class test
end
var obj = test("Hello world")
for (var i = 1; i <= 10; i=i+1) do
for (var i = 1; i <= 1; i=i+1) do
obj.print(i)
end