go reuses frames
This commit is contained in:
@@ -1970,33 +1970,48 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
|
||||
if (fn->kind == JS_FUNC_KIND_REGISTER) {
|
||||
/* Register function: tail call by replacing current frame */
|
||||
JSCodeRegister *fn_code = fn->u.reg.code;
|
||||
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots);
|
||||
if (!new_frame) {
|
||||
int current_slots = (int)objhdr_cap56(frame->header);
|
||||
|
||||
if (fn_code->nr_slots <= current_slots) {
|
||||
/* FAST PATH: reuse current frame — no allocation */
|
||||
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
|
||||
frame->slots[0] = fr->slots[0]; /* this */
|
||||
for (int i = 0; i < copy_count; i++)
|
||||
frame->slots[1 + i] = fr->slots[1 + i];
|
||||
/* Null out remaining slots (locals/temps) */
|
||||
for (int i = 1 + copy_count; i < current_slots; i++)
|
||||
frame->slots[i] = JS_NULL;
|
||||
frame->function = fn_val;
|
||||
/* caller stays the same — we're reusing this frame */
|
||||
code = fn_code;
|
||||
env = fn->u.reg.env_record;
|
||||
pc = code->entry_point;
|
||||
} else {
|
||||
/* SLOW PATH: callee needs more slots, must allocate */
|
||||
JSFrameRegister *new_frame = alloc_frame_register(ctx, fn_code->nr_slots);
|
||||
if (!new_frame) {
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
goto disrupt;
|
||||
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
fn_val = fr->function;
|
||||
fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
fn_code = fn->u.reg.code;
|
||||
new_frame->function = fn_val;
|
||||
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
|
||||
new_frame->slots[0] = fr->slots[0]; /* this */
|
||||
for (int i = 0; i < copy_count; i++)
|
||||
new_frame->slots[1 + i] = fr->slots[1 + i];
|
||||
new_frame->caller = frame->caller;
|
||||
frame->caller = JS_NULL;
|
||||
frame = new_frame;
|
||||
frame_ref.val = JS_MKPTR(frame);
|
||||
code = fn_code;
|
||||
env = fn->u.reg.env_record;
|
||||
pc = code->entry_point;
|
||||
}
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||
fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
|
||||
fn_val = fr->function;
|
||||
fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
fn_code = fn->u.reg.code;
|
||||
new_frame->function = fn_val;
|
||||
/* Copy this + args from call frame to new frame */
|
||||
int copy_count = (c_argc < fn_code->arity) ? c_argc : fn_code->arity;
|
||||
new_frame->slots[0] = fr->slots[0]; /* this */
|
||||
for (int i = 0; i < copy_count; i++)
|
||||
new_frame->slots[1 + i] = fr->slots[1 + i];
|
||||
/* Tail call: callee returns to OUR caller, not to us */
|
||||
new_frame->caller = frame->caller;
|
||||
frame->caller = JS_NULL; /* detach current frame */
|
||||
/* Switch to callee */
|
||||
frame = new_frame;
|
||||
frame_ref.val = JS_MKPTR(frame);
|
||||
code = fn_code;
|
||||
env = fn->u.reg.env_record;
|
||||
pc = code->entry_point;
|
||||
} else {
|
||||
/* C/bytecode function: call it, then return result to our caller */
|
||||
ctx->reg_current_frame = frame_ref.val;
|
||||
|
||||
@@ -1492,6 +1492,10 @@ void gc_scan_object (JSContext *ctx, void *ptr, uint8_t *from_base, uint8_t *fro
|
||||
allow_grow: if true, grow heap when recovery is poor
|
||||
alloc_size: the allocation that triggered GC — used to size the new block */
|
||||
int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
||||
#ifdef DUMP_GC_TIMING
|
||||
struct timespec gc_t0, gc_t1;
|
||||
clock_gettime(CLOCK_MONOTONIC, &gc_t0);
|
||||
#endif
|
||||
JSRuntime *rt = ctx->rt;
|
||||
size_t old_used = ctx->heap_free - ctx->heap_base;
|
||||
size_t old_heap_size = ctx->current_block_size;
|
||||
@@ -1692,6 +1696,16 @@ int ctx_gc (JSContext *ctx, int allow_grow, size_t alloc_size) {
|
||||
ctx->gc_bytes_copied += new_used;
|
||||
size_t recovered = old_used > new_used ? old_used - new_used : 0;
|
||||
|
||||
#ifdef DUMP_GC_TIMING
|
||||
clock_gettime(CLOCK_MONOTONIC, &gc_t1);
|
||||
double gc_ms = (gc_t1.tv_sec - gc_t0.tv_sec) * 1000.0 +
|
||||
(gc_t1.tv_nsec - gc_t0.tv_nsec) / 1e6;
|
||||
fprintf(stderr, "GC #%u: %.2f ms | copied %zu KB | old %zu KB -> new %zu KB | recovered %zu KB (%.0f%%)\n",
|
||||
ctx->gc_count, gc_ms,
|
||||
new_used / 1024, old_used / 1024, new_size / 1024,
|
||||
recovered / 1024,
|
||||
old_used > 0 ? (100.0 * recovered / old_used) : 0.0);
|
||||
#endif
|
||||
|
||||
ctx->heap_base = to_base;
|
||||
ctx->heap_free = to_free;
|
||||
|
||||
Reference in New Issue
Block a user