faster aot
This commit is contained in:
2
build.cm
2
build.cm
@@ -81,7 +81,7 @@ function content_hash(str) {
|
||||
}
|
||||
|
||||
// Bump when native codegen/runtime ABI changes so stale dylibs are not reused.
|
||||
def NATIVE_CACHE_VERSION = "native-v8"
|
||||
def NATIVE_CACHE_VERSION = "native-v16"
|
||||
|
||||
// Enable AOT ASan by creating .cell/asan_aot in the package root.
|
||||
function native_sanitize_flags() {
|
||||
|
||||
6
qbe.cm
6
qbe.cm
@@ -11,7 +11,7 @@ def js_null = 7
|
||||
def js_false = 3
|
||||
def js_true = 35
|
||||
def js_exception = 15
|
||||
def js_empty_text = 27
|
||||
def js_empty_text = 11
|
||||
|
||||
// Shared closure vars for functions with >4 params
|
||||
var _qop = null
|
||||
@@ -67,13 +67,13 @@ var is_ptr = function(p, v) {
|
||||
|
||||
var is_imm_text = function(p, v) {
|
||||
return ` %${p}.t =l and ${v}, 31
|
||||
%${p} =w ceql %${p}.t, 27
|
||||
%${p} =w ceql %${p}.t, 11
|
||||
`
|
||||
}
|
||||
|
||||
var is_text = function(p, v) {
|
||||
return ` %${p}.imm =l and ${v}, 31
|
||||
%${p}.is_imm =w ceql %${p}.imm, 27
|
||||
%${p}.is_imm =w ceql %${p}.imm, 11
|
||||
jnz %${p}.is_imm, @${p}.yes, @${p}.chk_ptr
|
||||
@${p}.chk_ptr
|
||||
%${p}.ptag =l and ${v}, 7
|
||||
|
||||
1005
qbe_emit.cm
1005
qbe_emit.cm
File diff suppressed because it is too large
Load Diff
@@ -230,23 +230,188 @@ JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
|
||||
|
||||
/* --- Property access --- */
|
||||
|
||||
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
|
||||
/* Current module handle for active native dispatch. */
|
||||
static CELL_THREAD_LOCAL void *g_current_dl_handle = NULL;
|
||||
|
||||
typedef struct {
|
||||
void *dl_handle;
|
||||
JSContext *ctx;
|
||||
JSGCRef *vals;
|
||||
int count;
|
||||
} AOTLiteralPool;
|
||||
|
||||
static CELL_THREAD_LOCAL AOTLiteralPool g_aot_lit_pool = {0};
|
||||
|
||||
static void aot_clear_lit_pool(void) {
|
||||
if (g_aot_lit_pool.vals) {
|
||||
if (g_aot_lit_pool.ctx) {
|
||||
for (int i = 0; i < g_aot_lit_pool.count; i++)
|
||||
JS_DeleteGCRef(g_aot_lit_pool.ctx, &g_aot_lit_pool.vals[i]);
|
||||
}
|
||||
free(g_aot_lit_pool.vals);
|
||||
}
|
||||
g_aot_lit_pool.dl_handle = NULL;
|
||||
g_aot_lit_pool.ctx = NULL;
|
||||
g_aot_lit_pool.vals = NULL;
|
||||
g_aot_lit_pool.count = 0;
|
||||
}
|
||||
|
||||
static int aot_load_lit_pool(JSContext *ctx, void *dl_handle) {
|
||||
aot_clear_lit_pool();
|
||||
g_aot_lit_pool.dl_handle = dl_handle;
|
||||
g_aot_lit_pool.ctx = ctx;
|
||||
if (!dl_handle)
|
||||
return 1;
|
||||
|
||||
int *count_ptr = (int *)dlsym(dl_handle, "cell_lit_count");
|
||||
const char **table_ptr = (const char **)dlsym(dl_handle, "cell_lit_table");
|
||||
int count = count_ptr ? *count_ptr : 0;
|
||||
if (count <= 0 || !table_ptr)
|
||||
return 1;
|
||||
|
||||
g_aot_lit_pool.vals = (JSGCRef *)calloc((size_t)count, sizeof(JSGCRef));
|
||||
if (!g_aot_lit_pool.vals) {
|
||||
JS_RaiseOOM(ctx);
|
||||
return 0;
|
||||
}
|
||||
g_aot_lit_pool.count = 0;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const char *cstr = table_ptr[i] ? table_ptr[i] : "";
|
||||
JS_AddGCRef(ctx, &g_aot_lit_pool.vals[i]);
|
||||
g_aot_lit_pool.count = i + 1;
|
||||
g_aot_lit_pool.vals[i].val = js_key_new(ctx, cstr);
|
||||
if (JS_IsException(g_aot_lit_pool.vals[i].val)) {
|
||||
aot_clear_lit_pool();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static JSValue aot_lit_from_index(JSContext *ctx, int64_t lit_idx) {
|
||||
if (lit_idx < 0) {
|
||||
JS_RaiseDisrupt(ctx, "literal index out of range");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (g_aot_lit_pool.dl_handle != g_current_dl_handle || g_aot_lit_pool.ctx != ctx) {
|
||||
if (!aot_load_lit_pool(ctx, g_current_dl_handle))
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
if (lit_idx >= g_aot_lit_pool.count) {
|
||||
JS_RaiseDisrupt(ctx, "literal index out of range");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return g_aot_lit_pool.vals[lit_idx].val;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
JSValue key;
|
||||
} AOTKeyCacheEntry;
|
||||
|
||||
static CELL_THREAD_LOCAL JSContext *g_aot_key_cache_ctx = NULL;
|
||||
static CELL_THREAD_LOCAL AOTKeyCacheEntry *g_aot_key_cache = NULL;
|
||||
static CELL_THREAD_LOCAL int g_aot_key_cache_count = 0;
|
||||
static CELL_THREAD_LOCAL int g_aot_key_cache_cap = 0;
|
||||
|
||||
/* Convert a static C string to an interned JSValue key.
|
||||
Uses a small thread-local cache keyed by C-string pointer to avoid
|
||||
repeated UTF-8 decoding in hot property paths. */
|
||||
static JSValue aot_key_from_cstr(JSContext *ctx, const char *name) {
|
||||
if (!name)
|
||||
return JS_NULL;
|
||||
|
||||
if (g_aot_key_cache_ctx != ctx) {
|
||||
free(g_aot_key_cache);
|
||||
g_aot_key_cache = NULL;
|
||||
g_aot_key_cache_count = 0;
|
||||
g_aot_key_cache_cap = 0;
|
||||
g_aot_key_cache_ctx = ctx;
|
||||
}
|
||||
|
||||
for (int i = 0; i < g_aot_key_cache_count; i++) {
|
||||
if (g_aot_key_cache[i].name == name)
|
||||
return g_aot_key_cache[i].key;
|
||||
}
|
||||
|
||||
JSValue key = js_key_new(ctx, name);
|
||||
if (JS_IsNull(key))
|
||||
return JS_RaiseDisrupt(ctx, "invalid property key");
|
||||
|
||||
if (g_aot_key_cache_count >= g_aot_key_cache_cap) {
|
||||
int new_cap = g_aot_key_cache_cap ? (g_aot_key_cache_cap * 2) : 64;
|
||||
AOTKeyCacheEntry *new_cache =
|
||||
(AOTKeyCacheEntry *)realloc(g_aot_key_cache, (size_t)new_cap * sizeof(*new_cache));
|
||||
if (!new_cache)
|
||||
return JS_RaiseOOM(ctx);
|
||||
g_aot_key_cache = new_cache;
|
||||
g_aot_key_cache_cap = new_cap;
|
||||
}
|
||||
|
||||
g_aot_key_cache[g_aot_key_cache_count].name = name;
|
||||
g_aot_key_cache[g_aot_key_cache_count].key = key;
|
||||
g_aot_key_cache_count++;
|
||||
return key;
|
||||
}
|
||||
|
||||
static JSValue cell_rt_load_field_key(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
if (JS_IsFunction(obj)) {
|
||||
JS_RaiseDisrupt(ctx, "cannot read property of function");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return JS_GetPropertyStr(ctx, obj, name);
|
||||
return JS_GetProperty(ctx, obj, key);
|
||||
}
|
||||
|
||||
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_load_field_key(ctx, obj, key);
|
||||
}
|
||||
|
||||
JSValue cell_rt_load_field_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_load_field_key(ctx, obj, key);
|
||||
}
|
||||
|
||||
/* Like cell_rt_load_field but without the function guard.
|
||||
Used by load_dynamic when the key happens to be a static string. */
|
||||
JSValue cell_rt_load_prop_str(JSContext *ctx, JSValue obj, const char *name) {
|
||||
return JS_GetPropertyStr(ctx, obj, name);
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return JS_GetProperty(ctx, obj, key);
|
||||
}
|
||||
|
||||
void cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
|
||||
const char *name) {
|
||||
JS_SetPropertyStr(ctx, obj, name, val);
|
||||
static int cell_rt_store_field_key(JSContext *ctx, JSValue val, JSValue obj,
|
||||
JSValue key) {
|
||||
int ret = JS_SetProperty(ctx, obj, key, val);
|
||||
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
||||
}
|
||||
|
||||
int cell_rt_store_field(JSContext *ctx, JSValue val, JSValue obj,
|
||||
const char *name) {
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return 0;
|
||||
return cell_rt_store_field_key(ctx, val, obj, key);
|
||||
}
|
||||
|
||||
int cell_rt_store_field_lit(JSContext *ctx, JSValue val, JSValue obj,
|
||||
int64_t lit_idx) {
|
||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||
if (JS_IsException(key))
|
||||
return 0;
|
||||
return cell_rt_store_field_key(ctx, val, obj, key);
|
||||
}
|
||||
|
||||
JSValue cell_rt_access_lit(JSContext *ctx, int64_t lit_idx) {
|
||||
return aot_lit_from_index(ctx, lit_idx);
|
||||
}
|
||||
|
||||
JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
@@ -255,16 +420,22 @@ JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
return JS_GetProperty(ctx, obj, key);
|
||||
}
|
||||
|
||||
void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
|
||||
JSValue key) {
|
||||
int cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
|
||||
JSValue key) {
|
||||
int ret = 0;
|
||||
JSValue nr = JS_NULL;
|
||||
if (JS_IsInt(key)) {
|
||||
JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
|
||||
nr = JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
|
||||
return JS_IsException(nr) ? 0 : 1;
|
||||
} else if (JS_IsArray(obj) && !JS_IsInt(key)) {
|
||||
JS_RaiseDisrupt(ctx, "array index must be a number");
|
||||
return 0;
|
||||
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) {
|
||||
JS_RaiseDisrupt(ctx, "object key must be text");
|
||||
return 0;
|
||||
} else {
|
||||
JS_SetProperty(ctx, obj, key, val);
|
||||
ret = JS_SetProperty(ctx, obj, key, val);
|
||||
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,12 +445,17 @@ JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
|
||||
return JS_GetProperty(ctx, arr, idx);
|
||||
}
|
||||
|
||||
void cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
|
||||
JSValue idx) {
|
||||
int cell_rt_store_index(JSContext *ctx, JSValue val, JSValue arr,
|
||||
JSValue idx) {
|
||||
int ret = 0;
|
||||
JSValue nr = JS_NULL;
|
||||
if (JS_IsInt(idx))
|
||||
JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
|
||||
nr = JS_SetPropertyNumber(ctx, arr, (uint32_t)JS_VALUE_GET_INT(idx), val);
|
||||
else
|
||||
JS_SetProperty(ctx, arr, idx, val);
|
||||
ret = JS_SetProperty(ctx, arr, idx, val);
|
||||
if (JS_IsInt(idx))
|
||||
return JS_IsException(nr) ? 0 : 1;
|
||||
return (ret < 0 || JS_HasException(ctx)) ? 0 : 1;
|
||||
}
|
||||
|
||||
/* --- Intrinsic/global lookup --- */
|
||||
@@ -294,6 +470,17 @@ void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
||||
fprintf(stderr, "cell_rt_set_native_env: ERROR env not stone\n");
|
||||
abort();
|
||||
}
|
||||
/* Drop module literal pool roots before switching native env/module. */
|
||||
aot_clear_lit_pool();
|
||||
|
||||
/* Native module boundary: clear per-thread key cache so stale keys
|
||||
cannot survive across context/module lifetimes. */
|
||||
free(g_aot_key_cache);
|
||||
g_aot_key_cache = NULL;
|
||||
g_aot_key_cache_count = 0;
|
||||
g_aot_key_cache_cap = 0;
|
||||
g_aot_key_cache_ctx = ctx;
|
||||
|
||||
if (g_has_native_env)
|
||||
JS_DeleteGCRef(ctx, &g_native_env_ref);
|
||||
if (!JS_IsNull(env)) {
|
||||
@@ -305,10 +492,10 @@ void cell_rt_set_native_env(JSContext *ctx, JSValue env) {
|
||||
}
|
||||
}
|
||||
|
||||
JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
|
||||
static JSValue cell_rt_get_intrinsic_key(JSContext *ctx, JSValue key) {
|
||||
/* Check native env first (runtime-provided functions like log) */
|
||||
if (g_has_native_env) {
|
||||
JSValue v = JS_GetPropertyStr(ctx, g_native_env_ref.val, name);
|
||||
JSValue v = JS_GetProperty(ctx, g_native_env_ref.val, key);
|
||||
if (!JS_IsNull(v))
|
||||
return v;
|
||||
}
|
||||
@@ -319,14 +506,28 @@ JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
|
||||
JSRecord *rec = (JSRecord *)chase(gobj);
|
||||
uint64_t mask = objhdr_cap56(rec->mist_hdr);
|
||||
for (uint64_t i = 1; i <= mask; i++) {
|
||||
if (js_key_equal_str(rec->slots[i].key, name))
|
||||
if (js_key_equal(rec->slots[i].key, key))
|
||||
return rec->slots[i].val;
|
||||
}
|
||||
}
|
||||
JS_RaiseDisrupt(ctx, "'%s' is not defined", name);
|
||||
JS_RaiseDisrupt(ctx, "name is not defined");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSValue cell_rt_get_intrinsic(JSContext *ctx, const char *name) {
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_get_intrinsic_key(ctx, key);
|
||||
}
|
||||
|
||||
JSValue cell_rt_get_intrinsic_lit(JSContext *ctx, int64_t lit_idx) {
|
||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_get_intrinsic_key(ctx, key);
|
||||
}
|
||||
|
||||
/* --- Closure access ---
|
||||
Walk the outer_frame chain on JSFunction (JS_FUNC_KIND_NATIVE).
|
||||
The frame's function field links to the JSFunction, whose
|
||||
@@ -436,6 +637,17 @@ JSValue *cell_rt_enter_frame(JSContext *ctx, int64_t nr_slots) {
|
||||
return (JSValue *)frame->slots;
|
||||
}
|
||||
|
||||
/* Push an already-allocated frame onto the active AOT frame stack. */
|
||||
static int cell_rt_push_existing_frame(JSContext *ctx, JSValue frame_val) {
|
||||
if (!ensure_aot_gc_ref_slot(ctx, g_aot_depth))
|
||||
return 0;
|
||||
JSGCRef *ref = aot_gc_ref_at(g_aot_depth);
|
||||
JS_AddGCRef(ctx, ref);
|
||||
ref->val = frame_val;
|
||||
g_aot_depth++;
|
||||
return 1;
|
||||
}
|
||||
|
||||
JSValue *cell_rt_refresh_fp(JSContext *ctx) {
|
||||
(void)ctx;
|
||||
if (g_aot_depth <= 0) {
|
||||
@@ -484,7 +696,7 @@ typedef JSValue (*cell_compiled_fn)(JSContext *ctx, void *fp);
|
||||
|
||||
/* Set before executing a native module's cell_main —
|
||||
used by cell_rt_make_function to resolve fn_ptr via dlsym */
|
||||
static CELL_THREAD_LOCAL void *g_current_dl_handle = NULL;
|
||||
/* g_current_dl_handle is defined near property/literal helpers. */
|
||||
|
||||
/* ============================================================
|
||||
Dispatch loop — the core of native function execution.
|
||||
@@ -604,8 +816,9 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
JS_PushGCRef(ctx, &callee_ref);
|
||||
callee_ref.val = callee_frame_val;
|
||||
JSFrameRegister *callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
|
||||
int callee_argc = (int)objhdr_cap56(callee_fr->header);
|
||||
callee_argc = (callee_argc >= 2) ? callee_argc - 2 : 0;
|
||||
int callee_argc = JS_VALUE_GET_INT(callee_fr->address);
|
||||
if (callee_argc < 0)
|
||||
callee_argc = 0;
|
||||
JSValue callee_fn_val = callee_fr->function;
|
||||
|
||||
if (!JS_IsFunction(callee_fn_val)) {
|
||||
@@ -625,47 +838,44 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
if (callee_fn->kind == JS_FUNC_KIND_NATIVE) {
|
||||
/* Native-to-native call — no C stack growth */
|
||||
cell_compiled_fn callee_ptr = (cell_compiled_fn)callee_fn->u.native.fn_ptr;
|
||||
int callee_slots = callee_fn->u.native.nr_slots;
|
||||
|
||||
if (pending_is_tail) {
|
||||
/* Tail call: replace frame instead of mutating in place.
|
||||
In-place reuse breaks closures that captured the caller frame. */
|
||||
/* Tail call: replace current frame with the prepared callee frame. */
|
||||
JSValue saved_caller = frame->caller;
|
||||
int cc = (callee_argc < callee_fn->length) ? callee_argc : callee_fn->length;
|
||||
if (cc < 0) cc = callee_argc;
|
||||
|
||||
/* Pop old frame */
|
||||
cell_rt_leave_frame(ctx);
|
||||
|
||||
/* Push new right-sized frame */
|
||||
JSValue *new_fp = cell_rt_enter_frame(ctx, callee_slots);
|
||||
if (!new_fp) {
|
||||
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
|
||||
callee_fn_val = callee_fn_ref.val;
|
||||
callee_fr->function = callee_fn_val;
|
||||
callee_fr->caller = saved_caller;
|
||||
callee_fr->address = JS_NewInt32(ctx, 0);
|
||||
|
||||
if (!cell_rt_push_existing_frame(ctx, callee_ref.val)) {
|
||||
JS_PopGCRef(ctx, &callee_fn_ref);
|
||||
JS_PopGCRef(ctx, &callee_ref);
|
||||
RETURN_DISPATCH(JS_EXCEPTION);
|
||||
}
|
||||
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
|
||||
JSFrameRegister *new_frame = (JSFrameRegister *)((char *)new_fp - offsetof(JSFrameRegister, slots));
|
||||
callee_fn_val = callee_fn_ref.val;
|
||||
new_frame->function = callee_fn_val;
|
||||
new_frame->caller = saved_caller;
|
||||
new_frame->slots[0] = callee_fr->slots[0];
|
||||
for (int i = 0; i < cc && i < callee_slots - 1; i++)
|
||||
new_frame->slots[1 + i] = callee_fr->slots[1 + i];
|
||||
frame = new_frame;
|
||||
fp = new_fp;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
|
||||
fp = (JSValue *)frame->slots;
|
||||
fn = callee_ptr;
|
||||
} else {
|
||||
/* Regular call: push new frame, link caller */
|
||||
/* Regular call: link caller and push prepared callee frame. */
|
||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||
int resume_seg = ret_info >> 16;
|
||||
int ret_slot = ret_info & 0xFFFF;
|
||||
|
||||
int cc = (callee_argc < callee_fn->length) ? callee_argc : callee_fn->length;
|
||||
if (cc < 0) cc = callee_argc;
|
||||
/* Save return address in caller */
|
||||
frame->address = JS_NewInt32(ctx, (resume_seg << 16) | ret_slot);
|
||||
|
||||
JSValue *new_fp = cell_rt_enter_frame(ctx, callee_slots);
|
||||
if (!new_fp) {
|
||||
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
|
||||
callee_fn_val = callee_fn_ref.val;
|
||||
callee_fr->function = callee_fn_val;
|
||||
callee_fr->caller = JS_MKPTR(frame);
|
||||
callee_fr->address = JS_NewInt32(ctx, 0);
|
||||
|
||||
if (!cell_rt_push_existing_frame(ctx, callee_ref.val)) {
|
||||
/* Resume caller with exception pending */
|
||||
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
||||
@@ -676,29 +886,8 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
JS_PopGCRef(ctx, &callee_ref);
|
||||
continue;
|
||||
}
|
||||
callee_fr = (JSFrameRegister *)JS_VALUE_GET_PTR(callee_ref.val);
|
||||
|
||||
/* Re-derive caller frame after alloc */
|
||||
if (g_aot_depth <= 1) {
|
||||
fprintf(stderr, "[BUG] native dispatch bad depth while linking caller: %d\n", g_aot_depth);
|
||||
abort();
|
||||
}
|
||||
frame_val = aot_gc_ref_at(g_aot_depth - 2)->val;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
||||
|
||||
JSFrameRegister *new_frame = (JSFrameRegister *)((char *)new_fp - offsetof(JSFrameRegister, slots));
|
||||
callee_fn_val = callee_fn_ref.val;
|
||||
new_frame->function = callee_fn_val;
|
||||
new_frame->caller = JS_MKPTR(frame);
|
||||
new_frame->slots[0] = callee_fr->slots[0];
|
||||
for (int i = 0; i < cc && i < callee_slots - 1; i++)
|
||||
new_frame->slots[1 + i] = callee_fr->slots[1 + i];
|
||||
|
||||
/* Save return address in caller */
|
||||
frame->address = JS_NewInt32(ctx, (resume_seg << 16) | ret_slot);
|
||||
|
||||
frame = new_frame;
|
||||
fp = new_fp;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(aot_gc_ref_at(g_aot_depth - 1)->val);
|
||||
fp = (JSValue *)frame->slots;
|
||||
fn = callee_ptr;
|
||||
}
|
||||
} else {
|
||||
@@ -719,11 +908,16 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
|
||||
if (JS_IsException(ret)) {
|
||||
/* Non-native callee threw — resume caller with exception pending.
|
||||
The caller's generated code checks JS_HasException at resume. */
|
||||
Tag the pending return slot with JS_EXCEPTION so generated code
|
||||
can branch without an extra JS_HasException C call. */
|
||||
if (!JS_HasException(ctx))
|
||||
JS_Disrupt(ctx);
|
||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||
int ret_slot = ret_info & 0xFFFF;
|
||||
if (ret_slot != 0xFFFF)
|
||||
fp[ret_slot] = JS_EXCEPTION;
|
||||
/* fn and fp still point to the calling native function's frame.
|
||||
Just resume it — it will detect the exception. */
|
||||
Just resume it — it will detect JS_EXCEPTION in the return slot. */
|
||||
JSFunction *exc_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
fn = (cell_compiled_fn)exc_fn->u.native.fn_ptr;
|
||||
JS_PopGCRef(ctx, &callee_ref);
|
||||
@@ -789,10 +983,14 @@ JSValue cell_native_dispatch(JSContext *ctx, JSValue func_obj,
|
||||
RETURN_DISPATCH(JS_EXCEPTION);
|
||||
}
|
||||
|
||||
/* Resume caller — it will check JS_HasException and branch to handler */
|
||||
/* Resume caller and tag the return slot with JS_EXCEPTION. */
|
||||
frame_val = aot_gc_ref_at(g_aot_depth - 1)->val;
|
||||
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
||||
fp = (JSValue *)frame->slots;
|
||||
int ret_info = JS_VALUE_GET_INT(frame->address);
|
||||
int ret_slot = ret_info & 0xFFFF;
|
||||
if (ret_slot != 0xFFFF)
|
||||
fp[ret_slot] = JS_EXCEPTION;
|
||||
|
||||
JSFunction *exc_caller_fn = JS_VALUE_GET_FUNCTION(frame->function);
|
||||
fn = (cell_compiled_fn)exc_caller_fn->u.native.fn_ptr;
|
||||
@@ -859,9 +1057,13 @@ JSValue cell_rt_frame(JSContext *ctx, JSValue fn, int64_t nargs) {
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
int nr_slots = (int)nargs + 2;
|
||||
JSFunction *f = JS_VALUE_GET_FUNCTION(fn);
|
||||
if (f->kind == JS_FUNC_KIND_NATIVE && f->u.native.nr_slots > nr_slots)
|
||||
nr_slots = f->u.native.nr_slots;
|
||||
JSFrameRegister *new_frame = alloc_frame_register(ctx, nr_slots);
|
||||
if (!new_frame) return JS_EXCEPTION;
|
||||
new_frame->function = fn;
|
||||
new_frame->address = JS_NewInt32(ctx, (int)nargs);
|
||||
return JS_MKPTR(new_frame);
|
||||
}
|
||||
|
||||
@@ -875,8 +1077,8 @@ void cell_rt_setarg(JSValue frame_val, int64_t idx, JSValue val) {
|
||||
JSValue cell_rt_invoke(JSContext *ctx, JSValue frame_val) {
|
||||
if (frame_val == JS_EXCEPTION) return JS_EXCEPTION;
|
||||
JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_val);
|
||||
int nr_slots = (int)objhdr_cap56(fr->header);
|
||||
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
|
||||
int c_argc = JS_VALUE_GET_INT(fr->address);
|
||||
if (c_argc < 0) c_argc = 0;
|
||||
JSValue fn_val = fr->function;
|
||||
|
||||
if (!JS_IsFunction(fn_val)) {
|
||||
@@ -933,14 +1135,27 @@ JSValue cell_rt_delete(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
return JS_NewBool(ctx, ret >= 0);
|
||||
}
|
||||
|
||||
JSValue cell_rt_delete_str(JSContext *ctx, JSValue obj, const char *name) {
|
||||
JSValue key = JS_NewString(ctx, name);
|
||||
static JSValue cell_rt_delete_key(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
int ret = JS_DeleteProperty(ctx, obj, key);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
return JS_NewBool(ctx, ret >= 0);
|
||||
}
|
||||
|
||||
JSValue cell_rt_delete_str(JSContext *ctx, JSValue obj, const char *name) {
|
||||
JSValue key = aot_key_from_cstr(ctx, name);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_delete_key(ctx, obj, key);
|
||||
}
|
||||
|
||||
JSValue cell_rt_delete_lit(JSContext *ctx, JSValue obj, int64_t lit_idx) {
|
||||
JSValue key = aot_lit_from_index(ctx, lit_idx);
|
||||
if (JS_IsException(key))
|
||||
return JS_EXCEPTION;
|
||||
return cell_rt_delete_key(ctx, obj, key);
|
||||
}
|
||||
|
||||
/* --- Typeof --- */
|
||||
|
||||
JSValue cell_rt_typeof(JSContext *ctx, JSValue val) {
|
||||
|
||||
Reference in New Issue
Block a user