Merge branch 'fix_gc' into mcode_streamline

This commit is contained in:
2026-02-13 05:59:11 -06:00
5 changed files with 282 additions and 382 deletions

View File

@@ -377,7 +377,7 @@ int cell_init(int argc, char **argv)
free(boot_data);
return 1;
}
JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, 16 * 1024 * 1024);
JSContext *ctx = JS_NewContextWithHeapSize(g_runtime, 1024 * 1024);
if (!ctx) {
printf("Failed to create JS context\n");
free(boot_data); JS_FreeRuntime(g_runtime);

View File

@@ -442,7 +442,7 @@ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count) {
if (!frame) return NULL;
/* cap56 = slot count (used by gc_object_size) */
frame->hdr = objhdr_make(slot_count, OBJ_FRAME, 0, 0, 0, 0);
frame->header = objhdr_make(slot_count, OBJ_FRAME, 0, 0, 0, 0);
frame->function = JS_NULL;
frame->caller = JS_NULL;
frame->address = JS_NewInt32(ctx, 0);
@@ -767,9 +767,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
/* Setup initial frame — wrap top-level code in a function object so that
returning from a called register function can read code/env from frame */
JSValue top_fn = js_new_register_function(ctx, code, env_gc.val, of_gc.val);
JS_PopGCRef(ctx, &of_gc);
env = env_gc.val; /* refresh — GC may have moved env during allocation */
JS_PopGCRef(ctx, &env_gc);
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
frame->function = top_fn;
frame->slots[0] = this_gc.val; /* slot 0 = this */
@@ -780,6 +778,8 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
}
for (int i = nargs_copy - 1; i >= 0; i--) JS_PopGCRef(ctx, &arg_gcs[i]);
JS_PopGCRef(ctx, &this_gc);
JS_PopGCRef(ctx, &of_gc);
JS_PopGCRef(ctx, &env_gc);
uint32_t pc = code->entry_point;
JSValue result = JS_NULL;
@@ -1653,7 +1653,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_INVOKE: {
/* A=frame_slot, B=result_slot */
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
int nr = (int)objhdr_cap56(fr->hdr);
int nr = (int)objhdr_cap56(fr->header);
int c_argc = (nr >= 2) ? nr - 2 : 0;
JSValue fn_val = fr->function;
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
@@ -1704,7 +1704,7 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code,
case MACH_GOINVOKE: {
/* Tail call: replace current frame with callee */
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]);
int nr = (int)objhdr_cap56(fr->hdr);
int nr = (int)objhdr_cap56(fr->header);
int c_argc = (nr >= 2) ? nr - 2 : 0;
JSValue fn_val = fr->function;
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);

View File

@@ -333,7 +333,7 @@ void cell_rt_setarg(JSValue frame_val, int64_t idx, JSValue val) {
JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
int nr_slots = (int)objhdr_cap56(fr->hdr);
int nr_slots = (int)objhdr_cap56(fr->header);
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
/* Copy args to C stack */

View File

@@ -115,6 +115,14 @@
#define gc_poison_region(addr, size) ((void)0)
#define gc_unpoison_region(addr, size) ((void)0)
#endif
#include <sys/mman.h>
#include <unistd.h>
static inline size_t poison_page_align(size_t size) {
size_t ps = (size_t)sysconf(_SC_PAGESIZE);
return (size + ps - 1) & ~(ps - 1);
}
#endif /* POISON_HEAP */
#ifdef HAVE_ASAN
@@ -361,13 +369,7 @@ struct JSClass {
#define JS_MODE_BACKTRACE_BARRIER \
(1 << 3) /* stop backtrace before this frame */
typedef struct JSFrameRegister {
objhdr_t hdr; // capacity in this is the total number of words of the object, including the 4 words of overhead and all slots
JSValue function; // JSFunction, function object being invoked
JSValue caller; // JSFrameRegister, the frame that called this one
JSValue address; // address of the instruction in the code that should be executed upon return
JSValue slots[]; // inline memory. order is [this][input args][closed over vars][non closed over vars][temporaries]
} JSFrameRegister; /// extra note: when this frame returns, caller should be set to 0. If caller is found to be 0, then the GC can reduce this frame's slots down to [this][input_args][closed over vars]; if no closed over vars it can be totally removed; may happen naturally in GC since it would have no refs?
/* JSFrameRegister is now an alias for JSFrame — see JSFrame definition below */
/* ============================================================
Register-Based VM Data Structures
@@ -823,17 +825,18 @@ typedef struct JSCodeRegister {
} JSCodeRegister;
/* Frame for closures - used by link-time relocation model where closures
reference outer frames via (depth, slot) addressing.
Stores function as JSValue to survive GC movements. */
/* Unified frame struct — used by the register VM and closures.
All fields are JSValues so the GC can scan them uniformly. */
typedef struct JSFrame {
objhdr_t header; /* OBJ_FRAME, cap56 = slot count */
JSValue function; /* JSValue for GC safety (use JS_VALUE_GET_FUNCTION) */
JSValue caller; /* JSValue for GC safety (unused currently) */
uint32_t return_pc;
JSValue function; /* JSFunction, function object being invoked */
JSValue caller; /* JSFrame, the frame that called this one */
JSValue address; /* return PC stored as JS_NewInt32 */
JSValue slots[]; /* [this][args][captured][locals][temps] */
} JSFrame;
typedef JSFrame JSFrameRegister;
static inline objhdr_t objhdr_set_s (objhdr_t h, bool s) {
return s ? (h | OBJHDR_S_MASK) : (h & ~OBJHDR_S_MASK);
}
@@ -903,7 +906,8 @@ typedef struct JSArray {
JSValue values[]; /* inline flexible array member */
} JSArray;
/* JSBlob - binary data per memory.md */
/* JSBlob — not allocated on GC heap (blobs use JSRecord + opaque).
Struct kept for reference; gc_object_size/gc_scan_object do not handle OBJ_BLOB. */
typedef struct JSBlob {
objhdr_t mist_hdr;
word_t length;
@@ -926,7 +930,7 @@ typedef slot JSRecordEntry;
typedef struct JSRecord {
objhdr_t mist_hdr;
struct JSRecord *proto;
JSValue proto; /* prototype as JSValue (JS_NULL if none) */
word_t len; /* number of entries */
slot slots[]; /* slots[0] reserved: key low32=class_id, key high32=rec_id, val=opaque */
} JSRecord;
@@ -1071,6 +1075,13 @@ static JS_BOOL JSText_equal_ascii (const JSText *text, JSValue imm) {
enough to call the interrupt callback often. */
#define JS_INTERRUPT_COUNTER_INIT 10000
/* Auto-rooted C call argv — GC updates values in-place */
typedef struct CCallRoot {
JSValue *argv; /* points to C-stack-local array */
int argc;
struct CCallRoot *prev; /* stack for nesting (C -> JS -> C -> ...) */
} CCallRoot;
struct JSContext {
JSRuntime *rt;
@@ -1102,6 +1113,7 @@ struct JSContext {
JSGCRef *top_gc_ref; /* used to reference temporary GC roots (stack top) */
JSGCRef *last_gc_ref; /* used to reference temporary GC roots (list) */
CCallRoot *c_call_root; /* stack of auto-rooted C call argv arrays */
int class_count; /* size of class_array and class_proto */
JSClass *class_array;
@@ -1234,7 +1246,7 @@ typedef struct JSRegExp {
/* Get prototype from object (works for both JSRecord and JSRecord since they
* share layout) */
#define JS_OBJ_GET_PROTO(p) ((JSRecord *)((JSRecord *)(p))->proto)
#define JS_OBJ_GET_PROTO(p) (JS_IsNull(((JSRecord *)(p))->proto) ? NULL : (JSRecord *)JS_VALUE_GET_PTR(((JSRecord *)(p))->proto))
/* Initial capacity for new records (mask = 7, 8 slots total) */
#define JS_RECORD_INITIAL_MASK 7

File diff suppressed because it is too large Load Diff