text/plain
•
27.16 KB
•
665 lines
/* Nujel - Copyright (C) 2020-2022 - Benjamin Vincent Schulenburg
* This project uses the MIT license, a copy should be included under /LICENSE
*/
#ifndef NUJEL_AMALGAMATION
#include "nujel-private.h"
#endif
#include <math.h>
#include <stdlib.h>
#include <string.h>
#if !defined(NUJEL_USE_JUMPTABLE)
#if defined(__GNUC__)
#define NUJEL_USE_JUMPTABLE 1
#endif
#endif
#if defined(_MSC_VER)
#define NORETURN
#else
#define NORETURN __attribute__((noreturn))
#endif
/* Read an encoded signed 16-bit offset at ip */
i64 lBytecodeGetOffset16(const lBytecodeOp *ip) {
const int x = (ip[0] << 8) | ip[1];
return (x < (1 << 15)) ? x : -((1 << 16) - x);
}
/* Build a list of length len in stack starting at sp */
static lVal lStackBuildList(lVal *stack, int sp, int len) {
if (unlikely(len == 0)) {
return NIL;
}
const int nsp = sp - len;
lVal ret = lCons(stack[nsp], NIL);
lVal t = ret;
stack = &stack[sp - (len - 1)];
for (int i = 0; i < (len - 1); i++) {
t.vList->cdr = lCons(stack[i], NIL);
t = t.vList->cdr;
}
return ret;
}
static void lBytecodeEnsureSufficientStack(lThread *ctx) {
const int closureSizeLeft = (ctx->closureStackSize - ctx->csp) - 1;
if (unlikely(closureSizeLeft < 16)) {
ctx->closureStackSize += 32;
lClosure **t = realloc(ctx->closureStack, ctx->closureStackSize * sizeof(lClosure *));
if (unlikely(t == NULL)) {
exit(56);
}
ctx->closureStack = t;
}
const int valueSizeLeft = ctx->valueStackSize - ctx->sp;
if (unlikely(valueSizeLeft < 32)) {
ctx->valueStackSize += 128;
lVal *t = realloc(ctx->valueStack, ctx->valueStackSize * sizeof(lVal));
if (unlikely(t == NULL)) {
exit(57);
}
ctx->valueStack = t;
}
}
#define vmdispatch(o) switch (o)
#define vmcase(l) case l:
#define vmbreak break
/* Evaluate ops within callingClosure after pushing args on the stack */
lVal lBytecodeEval(lClosure *callingClosure, lBytecodeArray *text) {
lBytecodeOp *ip;
lBytecodeArray *ops = text;
lClosure *c = callingClosure;
lThread ctx;
const lVal *lits = text->literals->data;
lVal exceptionThrownValue;
#ifdef NUJEL_USE_JUMPTABLE
#undef vmdispatch
#undef vmcase
#undef vmbreak
#define vmdispatch(x) goto *vmJumptable[x];
#define vmcase(label) l##label:
#define vmbreak vmdispatch(*ip++);
static const void *const vmJumptable[256] = {
&&llopNOP, &&llopRet, &&llopIntByte, &&llopIntAdd, &&llopApply, &&llopSetVal,
&&llopPushValExt, &&llopDefVal, &&llopDefValExt, &&llopJmp, &&llopJt, &&llopJf,
&&llopDup, &&llopDrop, &&llopGetVal, &&llopGetValExt, &&llopSetValExt, &&llopCar,
&&llopCdr, &&llopClosurePush, &&llopCons, &&llopLet, &&llopClosurePop, &&llopFnDynamic,
&&llopMacroDynamic, &&llopTry, &&llopPushVal, &&llopPushTrue, &&llopPushFalse, &&llopEval,
&&llopLessPred, &&llopLessEqPred, &&llopEqualPred, &&llopGreaterEqPred, &&llopGreaterPred, &&llopIncInt,
&&llopPushNil, &&llopAdd, &&llopSub, &&llopMul, &&llopDiv, &&llopRem,
&&llopZeroPred, &&llopRef, &&llopCadr, &&llopMutableEval, &&llopList, &&llopThrow};
#endif
ctx.closureStackSize = 256;
ctx.valueStackSize = 32;
ctx.closureStack = malloc(ctx.closureStackSize * sizeof(lClosure *));
ctx.valueStack = malloc(ctx.valueStackSize * sizeof(lVal));
ctx.csp = 0;
ctx.sp = 0;
ctx.closureStack[0] = c;
ctx.text = text;
const int RSP = lRootsGet();
lRootsClosurePush(callingClosure);
lRootsThreadPush(&ctx);
ip = ops->data;
lGarbageCollectIfNecessary();
while (true) {
#ifdef VM_RUNTIME_CHECKS
if (unlikely(ctx.csp >= ctx.closureStackSize - 1)) {
ctx.closureStackSize *= 2;
lClosure **newStack = realloc(ctx.closureStack, ctx.closureStackSize * sizeof(lClosure *));
if (unlikely(newStack == NULL)) {
goto topLevelNoReturn;
}
ctx.closureStack = newStack;
}
if (unlikely(ctx.sp >= ctx.valueStackSize - 1)) {
ctx.valueStackSize *= 2;
lVal **newStack = realloc(ctx.valueStack, ctx.valueStackSize * sizeof(lVal *));
if (unlikely(newStack == NULL)) {
goto topLevelNoReturn;
}
ctx.valueStack = newStack;
}
#endif
vmdispatch(*ip++) {
vmcase(lopNOP) vmbreak;
vmcase(lopIntByte) ctx.valueStack[ctx.sp++] = lValInt((i8)*ip++);
vmbreak;
vmcase(lopAdd) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.sp--;
if (likely(a.type == ltInt)) {
if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vInt += b.vInt;
} else if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1] = (lVal){ltFloat, .vFloat = b.vFloat + a.vInt};
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
}
} else if (likely(a.type == ltFloat)) {
if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1].vFloat += b.vFloat;
} else if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vFloat += b.vInt;
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
}
} else if (a.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
ctx.valueStack[ctx.sp - 1] = lValInt(0);
}
vmbreak;
}
vmcase(lopSub) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.sp--;
if (likely(a.type == ltInt)) {
if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vInt -= b.vInt;
} else if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1] = (lVal){ltFloat, .vFloat = a.vInt - b.vFloat};
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
ctx.valueStack[ctx.sp - 1].vInt = -ctx.valueStack[ctx.sp - 1].vInt;
}
} else if (likely(a.type == ltFloat)) {
if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1].vFloat -= b.vFloat;
} else if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vFloat -= b.vInt;
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
ctx.valueStack[ctx.sp - 1].vFloat = -ctx.valueStack[ctx.sp - 1].vFloat;
}
} else if (a.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
vmbreak;
}
vmcase(lopMul) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.sp--;
if (likely(a.type == ltInt)) {
if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vInt *= b.vInt;
} else if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1] = (lVal){ltFloat, .vFloat = a.vInt * b.vFloat};
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
} else if (likely(a.type == ltFloat)) {
if (likely(b.type == ltFloat)) {
ctx.valueStack[ctx.sp - 1].vFloat *= b.vFloat;
} else if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vFloat *= b.vInt;
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
} else if (a.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
ctx.valueStack[ctx.sp - 1] = lValInt(1);
}
vmbreak;
}
vmcase(lopDiv) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.sp--;
if (likely(a.type == ltInt)) {
if (likely(b.type == ltInt)) {
lVal r = lValFloat((float)a.vInt / (float)b.vInt);
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (likely(b.type == ltFloat)) {
lVal r = lValFloat((float)a.vInt / b.vFloat);
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
} else if (likely(a.type == ltFloat)) {
if (likely(b.type == ltFloat)) {
lVal r = lValFloat(a.vFloat / b.vFloat);
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (likely(b.type == ltInt)) {
lVal r = lValFloat(a.vFloat / (float)b.vInt);
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (b.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
} else if (a.type != ltNil) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
} else {
exceptionThrownValue = lValExceptionArity(a, 2);
goto throwException;
}
vmbreak;
}
vmcase(lopRem) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.sp--;
if (likely(a.type == ltInt)) {
if (likely(b.type == ltInt)) {
ctx.valueStack[ctx.sp - 1].vInt = a.vInt % b.vInt;
} else if (likely(b.type == ltFloat)) {
lVal r = lValFloat(fmod(a.vInt, b.vFloat));
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (unlikely(b.type != ltNil)) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
}
} else if (likely(a.type == ltFloat)) {
if (likely(b.type == ltFloat)) {
lVal r = lValFloat(fmod(a.vFloat, b.vFloat));
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (likely(b.type == ltInt)) {
lVal r = lValFloat(fmod(a.vFloat, b.vInt));
if (unlikely(r.type == ltException)) {
exceptionThrownValue = r;
goto throwException;
}
ctx.valueStack[ctx.sp - 1] = r;
} else if (unlikely(b.type != ltNil)) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
}
} else if (unlikely(a.type != ltNil)) {
exceptionThrownValue = lValExceptionNonNumeric(b);
goto throwException;
}
vmbreak;
}
vmcase(lopIntAdd) ctx.valueStack[ctx.sp - 2].vInt += ctx.valueStack[ctx.sp - 1].vInt;
ctx.sp--;
vmbreak;
vmcase(lopCons) ctx.valueStack[ctx.sp - 2] = lCons(ctx.valueStack[ctx.sp - 2], ctx.valueStack[ctx.sp - 1]);
ctx.sp--;
vmbreak;
vmcase(lopLessPred) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.valueStack[ctx.sp - 2] = lValBool(lValGreater(a, b) < 0);
ctx.sp--;
vmbreak;
}
vmcase(lopLessEqPred) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.valueStack[ctx.sp - 2] = lValBool(lValGreater(a, b) <= 0);
ctx.sp--;
vmbreak;
}
vmcase(lopEqualPred) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.valueStack[ctx.sp - 2] = lValBool(lValEqual(a, b));
ctx.sp--;
vmbreak;
}
vmcase(lopGreaterEqPred) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.valueStack[ctx.sp - 2] = lValBool(lValGreater(a, b) >= 0);
ctx.sp--;
vmbreak;
}
vmcase(lopGreaterPred) {
lVal a = ctx.valueStack[ctx.sp - 2];
lVal b = ctx.valueStack[ctx.sp - 1];
ctx.valueStack[ctx.sp - 2] = lValBool(lValGreater(a, b) > 0);
ctx.sp--;
vmbreak;
}
vmcase(lopPushNil) ctx.valueStack[ctx.sp++] = NIL;
vmbreak;
vmcase(lopPushTrue) ctx.valueStack[ctx.sp++] = lValBool(true);
vmbreak;
vmcase(lopPushFalse) ctx.valueStack[ctx.sp++] = lValBool(false);
vmbreak;
vmcase(lopPushValExt) {
const uint v = (ip[0] << 8) | (ip[1]);
ip += 2;
ctx.valueStack[ctx.sp++] = lits[v];
vmbreak;
}
vmcase(lopPushVal) {
const uint v = *ip++;
ctx.valueStack[ctx.sp++] = lits[v];
vmbreak;
}
vmcase(lopDup) ctx.sp++;
ctx.valueStack[ctx.sp - 1] = ctx.valueStack[ctx.sp - 2];
vmbreak;
vmcase(lopDrop) ctx.sp--;
vmbreak;
vmcase(lopJmp) lGarbageCollectIfNecessary();
ip += lBytecodeGetOffset16(ip) - 1;
vmbreak;
vmcase(lopJt) lGarbageCollectIfNecessary();
ip += castToBool(ctx.valueStack[--ctx.sp]) ? lBytecodeGetOffset16(ip) - 1 : 2;
vmbreak;
vmcase(lopJf) lGarbageCollectIfNecessary();
ip += !castToBool(ctx.valueStack[--ctx.sp]) ? lBytecodeGetOffset16(ip) - 1 : 2;
vmbreak;
vmcase(lopDefValExt) {
const uint v = (ip[0] << 8) | (ip[1]);
ip += 2;
lDefineClosureSym(c, lits[v].vSymbol, ctx.valueStack[ctx.sp - 1]);
vmbreak;
}
vmcase(lopDefVal) {
const uint v = *ip++;
lDefineClosureSym(c, lits[v].vSymbol, ctx.valueStack[ctx.sp - 1]);
vmbreak;
}
vmcase(lopGetValExt) {
const uint off = (ip[0] << 8) | (ip[1]);
ip += 2;
lVal v = lGetClosureSym(c, lits[off].vSymbol);
if (unlikely(v.type == ltException)) {
exceptionThrownValue = v;
goto throwException;
}
ctx.valueStack[ctx.sp++] = v;
vmbreak;
}
vmcase(lopGetVal) {
const uint off = *ip++;
lVal v = lGetClosureSym(c, lits[off].vSymbol);
if (unlikely(v.type == ltException)) {
exceptionThrownValue = v;
goto throwException;
}
ctx.valueStack[ctx.sp++] = v;
vmbreak;
}
vmcase(lopRef) {
lVal v = lGenericRef(c, ctx.valueStack[ctx.sp - 2], ctx.valueStack[ctx.sp - 1]);
if (unlikely(v.type == ltException)) {
exceptionThrownValue = v;
goto throwException;
}
ctx.valueStack[ctx.sp - 2] = v;
ctx.sp--;
vmbreak;
}
vmcase(lopSetValExt) {
const uint v = (ip[0] << 8) | (ip[1]);
ip += 2;
lSetClosureSym(c, lits[v].vSymbol, ctx.valueStack[ctx.sp - 1]);
vmbreak;
}
vmcase(lopSetVal) {
const uint v = *ip++;
lSetClosureSym(c, lits[v].vSymbol, ctx.valueStack[ctx.sp - 1]);
vmbreak;
}
vmcase(lopZeroPred) {
lVal a = ctx.valueStack[ctx.sp - 1];
bool p = false;
if (likely(a.type == ltInt)) {
p = a.vInt == 0;
} else if (a.type == ltFloat) {
p = a.vFloat == 0.0;
}
ctx.valueStack[ctx.sp - 1] = lValBool(p);
vmbreak;
}
vmcase(lopIncInt) if (likely(ctx.valueStack[ctx.sp - 1].type == ltInt)) {
ctx.valueStack[ctx.sp - 1] = lValInt(ctx.valueStack[ctx.sp - 1].vInt + 1);
}
vmbreak;
vmcase(lopCar) ctx.valueStack[ctx.sp - 1] = lCar(ctx.valueStack[ctx.sp - 1]);
vmbreak;
vmcase(lopCdr) ctx.valueStack[ctx.sp - 1] = lCdr(ctx.valueStack[ctx.sp - 1]);
vmbreak;
vmcase(lopCadr) ctx.valueStack[ctx.sp - 1] = lCadr(ctx.valueStack[ctx.sp - 1]);
vmbreak;
vmcase(lopList) {
int len = *ip++;
lVal cargs = lStackBuildList(ctx.valueStack, ctx.sp, len);
ctx.sp = ctx.sp - len;
ctx.valueStack[ctx.sp++] = cargs;
vmbreak;
}
vmcase(lopClosurePush) ctx.valueStack[ctx.sp++] = lValEnvironment(c);
vmbreak;
vmcase(lopLet) c = lClosureNew(c, closureLet);
c->type = closureLet;
ctx.closureStack[++ctx.csp] = c;
vmbreak;
vmcase(lopClosurePop) c = ctx.closureStack[--ctx.csp];
vmbreak;
vmcase(lopTry) c->ip = ip + lBytecodeGetOffset16(ip) - 1;
c->sp = ctx.sp;
c->text = ops;
c = lClosureNew(c, closureTry);
c->exceptionHandler = ctx.valueStack[--ctx.sp];
ctx.closureStack[++ctx.csp] = c;
ip += 2;
vmbreak;
vmcase(lopThrow) {
lVal v = ctx.valueStack[ctx.sp - 1];
if (likely(v.type == ltPair)) {
v.type = ltException;
}
exceptionThrownValue = v;
throwException:
if (unlikely((exceptionThrownValue.type != ltPair) && (exceptionThrownValue.type != ltException))) {
exceptionThrownValue = lCons(exceptionThrownValue, NIL);
}
if (likely(exceptionThrownValue.type == ltException)) {
exceptionThrownValue.type = ltPair;
lPair *t = exceptionThrownValue.vList;
while (t->cdr.type == ltPair) {
t = t->cdr.vList;
}
t->cdr = lCons(lValLambda(c), NIL);
}
while (c->type != closureTry) {
if (unlikely(ctx.csp <= 0)) {
free(ctx.closureStack);
free(ctx.valueStack);
lRootsRet(RSP);
exceptionThrownValue.type = ltException;
return exceptionThrownValue;
}
c = ctx.closureStack[--ctx.csp];
}
lVal handler = c->exceptionHandler;
lVal nv = lApply(c, lCons(exceptionThrownValue, NIL), handler);
if (unlikely(nv.type == ltException)) {
exceptionThrownValue = nv;
c = ctx.closureStack[--ctx.csp];
goto throwException;
}
c = ctx.closureStack[--ctx.csp]; // We can't do this before the
// apply since otherwise the GC
// might collect the handler
ops = c->text;
lits = ops->literals->data;
ctx.text = ops;
ip = c->ip;
ctx.sp = c->sp;
ctx.valueStack[ctx.sp++] = nv;
vmbreak;
}
vmcase(lopMacroDynamic) vmcase(lopFnDynamic) {
const lBytecodeOp curOp = ip[-1];
lVal cBody = ctx.valueStack[--ctx.sp];
lVal cDocs = ctx.valueStack[--ctx.sp];
lVal cArgs = ctx.valueStack[--ctx.sp];
lVal fun = lLambdaNew(c, cArgs, cBody);
lClosureSetMeta(fun.vClosure, cDocs);
if (unlikely(curOp == lopMacroDynamic)) {
fun.type = ltMacro;
}
ctx.valueStack[ctx.sp++] = fun;
vmbreak;
}
vmcase(lopMutableEval) vmcase(lopEval) {
const lBytecodeOp curOp = ip[-1];
lVal env = ctx.valueStack[--ctx.sp];
lVal bc = ctx.valueStack[--ctx.sp];
if (unlikely((env.type != ltEnvironment) || (bc.type != ltBytecodeArr))) {
exceptionThrownValue = lValException("type-error", "Can't eval in that", env);
goto throwException;
}
c->text = ops;
c->sp = ctx.sp;
c->ip = ip;
if (unlikely(curOp == lopMutableEval)) {
c = ctx.closureStack[++ctx.csp] = env.vClosure;
} else {
c = ctx.closureStack[++ctx.csp] = lClosureNew(env.vClosure, closureCall);
}
c->text = bc.vBytecodeArr;
ip = c->ip = c->text->data;
ctx.text = ops = c->text;
lits = ops->literals->data;
lBytecodeEnsureSufficientStack(&ctx);
lGarbageCollectIfNecessary();
vmbreak;
}
vmcase(lopApply) {
int len = *ip++;
lVal cargs = lStackBuildList(ctx.valueStack, ctx.sp, len);
ctx.sp = ctx.sp - len;
lVal fun = ctx.valueStack[--ctx.sp];
switch (fun.type) {
case ltMacro:
case ltLambda:
c->text = ops;
c->sp = ctx.sp;
c->ip = ip;
ctx.closureStack[++ctx.csp] = lClosureNewFunCall(c, cargs, fun);
c = ctx.closureStack[ctx.csp];
ip = c->ip;
ctx.text = ops = c->text;
lits = ops->literals->data;
lBytecodeEnsureSufficientStack(&ctx);
lGarbageCollectIfNecessary();
break;
case ltNativeFunc: {
lVal v = fun.vNFunc->fp(c, cargs);
if (unlikely(v.type == ltException)) {
exceptionThrownValue = v;
goto throwException;
}
ctx.valueStack[ctx.sp++] = v;
break;
}
default: {
exceptionThrownValue = lValException("type-error", "Can't apply to following val", fun);
goto throwException;
}
}
vmbreak;
}
vmcase(lopRet) if (likely(ctx.csp > 0)) {
while (ctx.closureStack[ctx.csp]->type != closureCall) {
if (unlikely(--ctx.csp <= 0)) {
goto topLevelReturn;
}
}
lVal ret = ctx.valueStack[ctx.sp - 1];
c = ctx.closureStack[--ctx.csp];
ip = c->ip;
ops = c->text;
lits = ops->literals->data;
ctx.text = ops;
ctx.sp = c->sp;
ctx.valueStack[ctx.sp++] = ret;
vmbreak;
}
topLevelReturn : {
lVal ret = ctx.valueStack[ctx.sp - 1];
free(ctx.closureStack);
free(ctx.valueStack);
lRootsRet(RSP);
return ret;
}
}
}
}