From 9ebae876f639163a0d318887c7b657bfd682ca25 Mon Sep 17 00:00:00 2001 From: CPunch Date: Fri, 13 Nov 2020 17:39:47 -0600 Subject: [PATCH] minor refactoring, OP_INVOKE performance boost --- newtest.cosmo | 26 ++++++++++---- src/cbaselib.c | 1 + src/cdebug.c | 2 ++ src/cobj.c | 3 +- src/cobj.h | 3 +- src/coperators.h | 2 +- src/cparse.c | 22 +++--------- src/cvalue.h | 4 +-- src/cvm.c | 92 +++++++++++++++++++++++++++--------------------- test.cosmo | 2 +- 10 files changed, 86 insertions(+), 71 deletions(-) diff --git a/newtest.cosmo b/newtest.cosmo index e80efd4..792dd80 100644 --- a/newtest.cosmo +++ b/newtest.cosmo @@ -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()) \ No newline at end of file +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 \ No newline at end of file diff --git a/src/cbaselib.c b/src/cbaselib.c index 44fdb3c..5bf7838 100644 --- a/src/cbaselib.c +++ b/src/cbaselib.c @@ -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); diff --git a/src/cdebug.c b/src/cdebug.c index 2a903fe..55e6b7e 100644 --- a/src/cdebug.c +++ b/src/cdebug.c @@ -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: diff --git a/src/cobj.c b/src/cobj.c index 111daae..ea7df1b 100644 --- a/src/cobj.c +++ b/src/cobj.c @@ -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); diff --git a/src/cobj.h b/src/cobj.h index 0c00d16..ddd3a56 100644 --- a/src/cobj.h +++ b/src/cobj.h @@ -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; diff --git a/src/coperators.h b/src/coperators.h index d346cc4..1b814fa 100644 --- a/src/coperators.h +++ b/src/coperators.h @@ -32,7 +32,7 @@ typedef enum { OP_NEWOBJECT, OP_GETOBJECT, OP_SETOBJECT, - OP_NEWCLASS, + OP_INVOKE, // ARITHMETIC OP_ADD, diff --git a/src/cparse.c b/src/cparse.c index abb2fcf..d878d24 100644 --- a/src/cparse.c +++ b/src/cparse.c @@ -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); diff --git a/src/cvalue.h b/src/cvalue.h index a6f2c0c..176e7b4 100644 --- a/src/cvalue.h +++ b/src/cvalue.h @@ -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; diff --git a/src/cvm.c b/src/cvm.c index 0374954..596704e 100644 --- a/src/cvm.c +++ b/src/cvm.c @@ -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; diff --git a/test.cosmo b/test.cosmo index 3029113..ba09f7b 100644 --- a/test.cosmo +++ b/test.cosmo @@ -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