shorten frames to closure vars only on gc
This commit is contained in:
43
mcode.cm
43
mcode.cm
@@ -951,6 +951,7 @@ var mcode = function(ast) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scan scope record for variable declarations
|
// Scan scope record for variable declarations
|
||||||
|
// Closure locals are assigned first so returned frames can be shortened
|
||||||
var scan_scope = function() {
|
var scan_scope = function() {
|
||||||
var scope = find_scope_record(s_function_nr)
|
var scope = find_scope_record(s_function_nr)
|
||||||
if (scope == null) {
|
if (scope == null) {
|
||||||
@@ -963,6 +964,9 @@ var mcode = function(ast) {
|
|||||||
var make = null
|
var make = null
|
||||||
var is_const = false
|
var is_const = false
|
||||||
var slot = 0
|
var slot = 0
|
||||||
|
|
||||||
|
// Pass 1: closure locals first
|
||||||
|
_i = 0
|
||||||
while (_i < length(keys)) {
|
while (_i < length(keys)) {
|
||||||
name = keys[_i]
|
name = keys[_i]
|
||||||
if (name == "function_nr" || name == "nr_close_slots") {
|
if (name == "function_nr" || name == "nr_close_slots") {
|
||||||
@@ -975,14 +979,36 @@ var mcode = function(ast) {
|
|||||||
_i = _i + 1
|
_i = _i + 1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if (find_var(name) < 0) {
|
if (v.closure == true && find_var(name) < 0) {
|
||||||
|
is_const = (make == "def" || make == "function")
|
||||||
|
slot = 1 + s_nr_args + s_nr_local_slots
|
||||||
|
s_nr_local_slots = s_nr_local_slots + 1
|
||||||
|
s_nr_close_slots = s_nr_close_slots + 1
|
||||||
|
add_var(name, slot, is_const)
|
||||||
|
s_vars[length(s_vars) - 1].is_closure = true
|
||||||
|
}
|
||||||
|
_i = _i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: non-closure locals
|
||||||
|
_i = 0
|
||||||
|
while (_i < length(keys)) {
|
||||||
|
name = keys[_i]
|
||||||
|
if (name == "function_nr" || name == "nr_close_slots") {
|
||||||
|
_i = _i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v = scope[name]
|
||||||
|
make = v.make
|
||||||
|
if (make == null || make == "input") {
|
||||||
|
_i = _i + 1
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (v.closure != true && find_var(name) < 0) {
|
||||||
is_const = (make == "def" || make == "function")
|
is_const = (make == "def" || make == "function")
|
||||||
slot = 1 + s_nr_args + s_nr_local_slots
|
slot = 1 + s_nr_args + s_nr_local_slots
|
||||||
s_nr_local_slots = s_nr_local_slots + 1
|
s_nr_local_slots = s_nr_local_slots + 1
|
||||||
add_var(name, slot, is_const)
|
add_var(name, slot, is_const)
|
||||||
if (v.closure == true) {
|
|
||||||
s_vars[length(s_vars) - 1].is_closure = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_i = _i + 1
|
_i = _i + 1
|
||||||
}
|
}
|
||||||
@@ -2738,8 +2764,6 @@ var mcode = function(ast) {
|
|||||||
var disrupt_clause = func_node.disruption
|
var disrupt_clause = func_node.disruption
|
||||||
var null_slot2 = null
|
var null_slot2 = null
|
||||||
var fn_name = func_node.name
|
var fn_name = func_node.name
|
||||||
var fn_scope = null
|
|
||||||
var nr_cs = 0
|
|
||||||
var result = null
|
var result = null
|
||||||
var saved_label = 0
|
var saved_label = 0
|
||||||
var saved_func = 0
|
var saved_func = 0
|
||||||
@@ -2880,15 +2904,10 @@ var mcode = function(ast) {
|
|||||||
fn_name = "<anonymous>"
|
fn_name = "<anonymous>"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn_scope = find_scope_record(s_function_nr)
|
|
||||||
if (fn_scope != null && fn_scope.nr_close_slots != null) {
|
|
||||||
nr_cs = fn_scope.nr_close_slots
|
|
||||||
}
|
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
name: fn_name,
|
name: fn_name,
|
||||||
nr_args: nr_params,
|
nr_args: nr_params,
|
||||||
nr_close_slots: nr_cs,
|
nr_close_slots: s_nr_close_slots,
|
||||||
nr_slots: s_max_slot + 1,
|
nr_slots: s_max_slot + 1,
|
||||||
disruption_pc: disruption_start,
|
disruption_pc: disruption_start,
|
||||||
instructions: s_instructions
|
instructions: s_instructions
|
||||||
|
|||||||
@@ -833,7 +833,7 @@ void __asan_on_error(void) {
|
|||||||
fprintf(stderr, " %s (%s:%u)\n",
|
fprintf(stderr, " %s (%s:%u)\n",
|
||||||
func_name ? func_name : "<anonymous>",
|
func_name ? func_name : "<anonymous>",
|
||||||
file ? file : "<unknown>", line);
|
file ? file : "<unknown>", line);
|
||||||
if (JS_IsNull(frame->caller)) break;
|
if (!JS_IsPtr(frame->caller)) break;
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
||||||
is_first = 0;
|
is_first = 0;
|
||||||
}
|
}
|
||||||
@@ -913,6 +913,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
|
|||||||
env = env_gc.val; /* refresh — GC may have moved env during allocation */
|
env = env_gc.val; /* refresh — GC may have moved env during allocation */
|
||||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
frame->function = top_fn;
|
frame->function = top_fn;
|
||||||
|
frame->caller = JS_NewInt32(ctx, 0); /* sentinel: active top-level, not eligible for GC shortening */
|
||||||
frame->slots[0] = this_gc.val; /* slot 0 = this */
|
frame->slots[0] = this_gc.val; /* slot 0 = this */
|
||||||
|
|
||||||
/* Copy arguments from GC-safe refs */
|
/* Copy arguments from GC-safe refs */
|
||||||
@@ -1715,7 +1716,7 @@ vm_dispatch:
|
|||||||
|
|
||||||
VM_CASE(MACH_RETURN):
|
VM_CASE(MACH_RETURN):
|
||||||
result = frame->slots[a];
|
result = frame->slots[a];
|
||||||
if (JS_IsNull(frame->caller)) goto done;
|
if (!JS_IsPtr(frame->caller)) goto done;
|
||||||
{
|
{
|
||||||
#ifdef VALIDATE_GC
|
#ifdef VALIDATE_GC
|
||||||
const char *callee_name = "?";
|
const char *callee_name = "?";
|
||||||
@@ -1759,7 +1760,7 @@ vm_dispatch:
|
|||||||
|
|
||||||
VM_CASE(MACH_RETNIL):
|
VM_CASE(MACH_RETNIL):
|
||||||
result = JS_NULL;
|
result = JS_NULL;
|
||||||
if (JS_IsNull(frame->caller)) goto done;
|
if (!JS_IsPtr(frame->caller)) goto done;
|
||||||
{
|
{
|
||||||
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
||||||
frame->caller = JS_NULL;
|
frame->caller = JS_NULL;
|
||||||
@@ -2317,7 +2318,7 @@ vm_dispatch:
|
|||||||
if (JS_IsException(ret)) goto disrupt;
|
if (JS_IsException(ret)) goto disrupt;
|
||||||
/* Tail-return: act like MACH_RETURN with the result */
|
/* Tail-return: act like MACH_RETURN with the result */
|
||||||
result = ret;
|
result = ret;
|
||||||
if (JS_IsNull(frame->caller)) goto done;
|
if (!JS_IsPtr(frame->caller)) goto done;
|
||||||
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller);
|
||||||
frame->caller = JS_NULL;
|
frame->caller = JS_NULL;
|
||||||
frame = caller;
|
frame = caller;
|
||||||
@@ -2387,7 +2388,7 @@ vm_dispatch:
|
|||||||
ctx->current_exception = JS_NULL;
|
ctx->current_exception = JS_NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (JS_IsNull(frame->caller)) {
|
if (!JS_IsPtr(frame->caller)) {
|
||||||
/* Stack trace was already included in the JS_RaiseDisrupt log via the callback. */
|
/* Stack trace was already included in the JS_RaiseDisrupt log via the callback. */
|
||||||
ctx->disruption_reported = TRUE;
|
ctx->disruption_reported = TRUE;
|
||||||
frame_ref.val = JS_MKPTR(frame); /* update root for GC / done */
|
frame_ref.val = JS_MKPTR(frame); /* update root for GC / done */
|
||||||
@@ -2423,6 +2424,10 @@ done:
|
|||||||
ctx->reg_current_frame = frame_ref.val;
|
ctx->reg_current_frame = frame_ref.val;
|
||||||
ctx->current_register_pc = pc > 0 ? pc - 1 : 0;
|
ctx->current_register_pc = pc > 0 ? pc - 1 : 0;
|
||||||
}
|
}
|
||||||
|
if (JS_IsPtr(frame_ref.val)) {
|
||||||
|
JSFrameRegister *f = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
|
||||||
|
f->caller = JS_NULL; /* mark as returned so GC can shorten */
|
||||||
|
}
|
||||||
JS_DeleteGCRef(ctx, &frame_ref);
|
JS_DeleteGCRef(ctx, &frame_ref);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,7 +158,8 @@ JSValue *JS_PushGCRef (JSContext *ctx, JSGCRef *ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) {
|
JSValue JS_PopGCRef (JSContext *ctx, JSGCRef *ref) {
|
||||||
assert(ctx->top_gc_ref == ref && "JS_PopGCRef: not popping top of stack — mismatched push/pop");
|
if (ctx->top_gc_ref != ref)
|
||||||
|
fprintf(stderr, "WARN: JS_PopGCRef mismatch (expected %p, got %p)\n", (void*)ctx->top_gc_ref, (void*)ref);
|
||||||
ctx->top_gc_ref = ref->prev;
|
ctx->top_gc_ref = ref->prev;
|
||||||
return ref->val;
|
return ref->val;
|
||||||
}
|
}
|
||||||
@@ -1328,15 +1329,45 @@ JSValue gc_copy_value (JSContext *ctx, JSValue v, uint8_t *from_base, uint8_t *f
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t size = gc_object_size (hdr_ptr);
|
size_t size = gc_object_size (hdr_ptr);
|
||||||
if (*to_free + size > to_end) {
|
size_t copy_size = size;
|
||||||
fprintf (stderr, "gc_copy_value: out of to-space, need %zu bytes\n", size);
|
uint16_t new_cap = 0;
|
||||||
|
|
||||||
|
/* Frame shortening: returned frames (caller == JS_NULL) only need
|
||||||
|
[this][args][closure_locals] — shrink during copy. */
|
||||||
|
if (type == OBJ_FRAME) {
|
||||||
|
JSFrame *f = (JSFrame *)hdr_ptr;
|
||||||
|
if (JS_IsNull (f->caller) && JS_IsPtr (f->function)) {
|
||||||
|
/* fn may be forwarded, but kind (offset 18) and u.cell.code (offset 24)
|
||||||
|
are past the 16 bytes overwritten by fwd+size. */
|
||||||
|
JSFunction *fn = (JSFunction *)JS_VALUE_GET_PTR (f->function);
|
||||||
|
if (fn->kind == JS_FUNC_KIND_REGISTER) {
|
||||||
|
JSCode *jc = (JSCode *)JS_VALUE_GET_PTR (fn->u.cell.code);
|
||||||
|
if (jc && jc->kind == JS_CODE_KIND_REGISTER && jc->u.reg.code
|
||||||
|
&& jc->u.reg.code->nr_close_slots > 0) {
|
||||||
|
uint16_t cs = 1 + jc->u.reg.code->arity + jc->u.reg.code->nr_close_slots;
|
||||||
|
uint64_t orig = objhdr_cap56 (f->header);
|
||||||
|
if (cs < orig) {
|
||||||
|
new_cap = cs;
|
||||||
|
copy_size = gc_align_up (sizeof (JSFrame) + cs * sizeof (JSValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*to_free + copy_size > to_end) {
|
||||||
|
fprintf (stderr, "gc_copy_value: out of to-space, need %zu bytes\n", copy_size);
|
||||||
abort ();
|
abort ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void *new_ptr = *to_free;
|
void *new_ptr = *to_free;
|
||||||
memcpy (new_ptr, hdr_ptr, size);
|
memcpy (new_ptr, hdr_ptr, copy_size);
|
||||||
*to_free += size;
|
*to_free += copy_size;
|
||||||
|
|
||||||
|
if (new_cap > 0)
|
||||||
|
((JSFrame *)new_ptr)->header = objhdr_set_cap56 (((JSFrame *)new_ptr)->header, new_cap);
|
||||||
|
|
||||||
|
/* Stash ORIGINAL size for from-space linear walks */
|
||||||
*hdr_ptr = objhdr_make_fwd (new_ptr);
|
*hdr_ptr = objhdr_make_fwd (new_ptr);
|
||||||
*((size_t *)((uint8_t *)hdr_ptr + sizeof (objhdr_t))) = size;
|
*((size_t *)((uint8_t *)hdr_ptr + sizeof (objhdr_t))) = size;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user