feat: always wrap functions in closures to simplify VM and improve invocation performance

Simplify the VM by ensuring all called objects are closures, removing the need for runtime checks between bare functions and closures. This eliminates the `IS_FN`/`IS_CLOSURE` branching in `validateFn`, `checkArity`, `bindMethod`, and `wrenNewFiber`, and changes `CallFrame.fn` from `Obj*` to `ObjClosure*`. The compiler now emits `CODE_CLOSURE` for all function declarations, even those without upvalues, trading a small allocation cost at declaration time for faster and more uniform invocation. Benchmarks show performance is neutral to slightly improved across all tested workloads.
This commit is contained in:
Bob Nystrom
2016-03-26 21:00:17 +00:00
parent 82103a64f9
commit 19fc5e7534
8 changed files with 149 additions and 168 deletions
+12 -5
View File
@@ -12,17 +12,24 @@ void metaCompile(WrenVM* vm)
// Evaluate the code in the module where the calling function was defined.
// That's one stack frame back from the top since the top-most frame is the
// helper eval() method in Meta itself.
Value callingFn = OBJ_VAL(vm->fiber->frames[vm->fiber->numFrames - 2].fn);
ObjModule* module = IS_FN(callingFn)
? AS_FN(callingFn)->module
: AS_CLOSURE(callingFn)->fn->module;
ObjClosure* caller = vm->fiber->frames[vm->fiber->numFrames - 2].closure;
ObjModule* module = caller->fn->module;
// Compile it.
ObjFn* fn = wrenCompile(vm, module, wrenGetSlotString(vm, 1), false);
// Return the result. We can't use the public API for this since we have a
// bare ObjFn.
vm->apiStack[0] = fn != NULL ? OBJ_VAL(fn) : NULL_VAL;
if (fn == NULL)
{
vm->apiStack[0] = NULL_VAL;
}
else
{
wrenPushRoot(vm, (Obj*)fn);
vm->apiStack[0] = OBJ_VAL(wrenNewClosure(vm, fn));
wrenPopRoot(vm);
}
}
const char* wrenMetaSource()