native aot suite passes
This commit is contained in:
7
qbe.cm
7
qbe.cm
@@ -519,12 +519,9 @@ var ne_bool = function(p, a, b) {
|
||||
`
|
||||
}
|
||||
|
||||
// --- Type guard: is_identical ---
|
||||
// --- Type guard: is_identical (chases forwarding pointers via C helper) ---
|
||||
var is_identical = function(p, a, b) {
|
||||
return ` %${p}.cr =w ceql ${a}, ${b}
|
||||
%${p}.crext =l extuw %${p}.cr
|
||||
%${p}.sh =l shl %${p}.crext, 5
|
||||
%${p} =l or %${p}.sh, 3
|
||||
return ` %${p} =l call $cell_rt_is_identical(l %ctx, l ${a}, l ${b})
|
||||
`
|
||||
}
|
||||
|
||||
|
||||
19
qbe_emit.cm
19
qbe_emit.cm
@@ -102,6 +102,9 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
var pat_label = null
|
||||
var flg_label = null
|
||||
var in_handler = false
|
||||
var tol = null
|
||||
var fn_arity = 0
|
||||
var arity_tmp = null
|
||||
|
||||
// Function signature: (ctx, frame_ptr) → JSValue
|
||||
emit(`export function l $${name}(l %ctx, l %fp) {`)
|
||||
@@ -572,8 +575,10 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
if (op == "eq_tol" || op == "ne_tol") {
|
||||
lhs = s_read(a2)
|
||||
rhs = s_read(a3)
|
||||
a4 = instr[4]
|
||||
tol = s_read(a4)
|
||||
p = fresh()
|
||||
emit(` %${p} =l call $cell_rt_${op}(l %ctx, l ${lhs}, l ${rhs})`)
|
||||
emit(` %${p} =l call $cell_rt_${op}(l %ctx, l ${lhs}, l ${rhs}, l ${tol})`)
|
||||
s_write(a1, `%${p}`)
|
||||
continue
|
||||
}
|
||||
@@ -700,7 +705,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
p = fresh()
|
||||
if (pn != null) {
|
||||
sl = intern_str(pn)
|
||||
emit(` %${p} =l call $cell_rt_load_field(l %ctx, l ${v}, l ${sl})`)
|
||||
emit(` %${p} =l call $cell_rt_load_prop_str(l %ctx, l ${v}, l ${sl})`)
|
||||
} else {
|
||||
lhs = s_read(a3)
|
||||
emit(` %${p} =l call $cell_rt_load_dynamic(l %ctx, l ${v}, l ${lhs})`)
|
||||
@@ -899,8 +904,14 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
// --- Function object creation [G] ---
|
||||
|
||||
if (op == "function") {
|
||||
fn_arity = 0
|
||||
if (a2 >= 0 && a2 < length(ir.functions)) {
|
||||
fn_arity = ir.functions[a2].nr_args
|
||||
}
|
||||
p = fresh()
|
||||
emit(` %${p} =l call $cell_rt_make_function(l %ctx, l ${text(a2)}, l %fp)`)
|
||||
arity_tmp = fresh()
|
||||
emit(` %${arity_tmp} =l copy ${text(fn_arity)}`)
|
||||
emit(` %${p} =l call $cell_rt_make_function(l %ctx, l ${text(a2)}, l %fp, l %${arity_tmp})`)
|
||||
refresh_fp()
|
||||
s_write(a1, `%${p}`)
|
||||
continue
|
||||
@@ -980,7 +991,7 @@ var qbe_emit = function(ir, qbe, export_name) {
|
||||
p = fresh()
|
||||
if (pn != null) {
|
||||
sl = intern_str(pn)
|
||||
emit(` %${p} =l call $cell_rt_delete(l %ctx, l ${v}, l ${sl})`)
|
||||
emit(` %${p} =l call $cell_rt_delete_str(l %ctx, l ${v}, l ${sl})`)
|
||||
} else {
|
||||
lhs = s_read(a3)
|
||||
emit(` %${p} =l call $cell_rt_delete(l %ctx, l ${v}, l ${lhs})`)
|
||||
|
||||
@@ -222,6 +222,16 @@ JSValue qbe_shift_shr(JSContext *ctx, JSValue a, JSValue b) {
|
||||
/* --- Property access --- */
|
||||
|
||||
JSValue cell_rt_load_field(JSContext *ctx, JSValue obj, const char *name) {
|
||||
if (JS_IsFunction(obj)) {
|
||||
JS_ThrowTypeError(ctx, "cannot read property of function");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
return JS_GetPropertyStr(ctx, obj, name);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
@@ -238,10 +248,15 @@ JSValue cell_rt_load_dynamic(JSContext *ctx, JSValue obj, JSValue key) {
|
||||
|
||||
void cell_rt_store_dynamic(JSContext *ctx, JSValue val, JSValue obj,
|
||||
JSValue key) {
|
||||
if (JS_IsInt(key))
|
||||
if (JS_IsInt(key)) {
|
||||
JS_SetPropertyNumber(ctx, obj, (uint32_t)JS_VALUE_GET_INT(key), val);
|
||||
else
|
||||
} else if (JS_IsArray(obj) && !JS_IsInt(key)) {
|
||||
JS_ThrowTypeError(ctx, "array index must be a number");
|
||||
} else if (JS_IsBool(key) || JS_IsNull(key) || JS_IsArray(key) || JS_IsFunction(key)) {
|
||||
JS_ThrowTypeError(ctx, "object key must be text");
|
||||
} else {
|
||||
JS_SetProperty(ctx, obj, key, val);
|
||||
}
|
||||
}
|
||||
|
||||
JSValue cell_rt_load_index(JSContext *ctx, JSValue arr, JSValue idx) {
|
||||
@@ -466,7 +481,8 @@ static JSValue cell_fn_trampoline(JSContext *ctx, JSValue this_val,
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp) {
|
||||
JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp,
|
||||
int64_t nr_args) {
|
||||
(void)outer_fp;
|
||||
if (g_native_fn_count >= MAX_NATIVE_FN)
|
||||
return JS_ThrowTypeError(ctx, "too many native functions (max %d)", MAX_NATIVE_FN);
|
||||
@@ -487,7 +503,7 @@ JSValue cell_rt_make_function(JSContext *ctx, int64_t fn_idx, void *outer_fp) {
|
||||
}
|
||||
|
||||
return JS_NewCFunction2(ctx, (JSCFunction *)cell_fn_trampoline, "native_fn",
|
||||
255, JS_CFUNC_generic_magic, global_id);
|
||||
(int)nr_args, JS_CFUNC_generic_magic, global_id);
|
||||
}
|
||||
|
||||
/* --- Frame-based function calling --- */
|
||||
@@ -515,15 +531,35 @@ 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->header);
|
||||
int c_argc = (nr_slots >= 2) ? nr_slots - 2 : 0;
|
||||
JSValue fn_val = fr->function;
|
||||
|
||||
/* Copy args to C stack */
|
||||
JSValue args[c_argc > 0 ? c_argc : 1];
|
||||
for (int i = 0; i < c_argc; i++)
|
||||
args[i] = fr->slots[i + 1];
|
||||
if (!JS_IsFunction(fn_val)) {
|
||||
JS_ThrowTypeError(ctx, "not a function");
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
|
||||
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
|
||||
JSValue result;
|
||||
|
||||
if (fn->kind == JS_FUNC_KIND_C) {
|
||||
/* Match MACH_INVOKE: C functions go directly to js_call_c_function,
|
||||
bypassing JS_Call's arity check. Extra args are silently available. */
|
||||
result = js_call_c_function(ctx, fn_val, fr->slots[0], c_argc, &fr->slots[1]);
|
||||
} else {
|
||||
/* Register/bytecode functions — use JS_CallInternal (no arity gate) */
|
||||
JSValue args[c_argc > 0 ? c_argc : 1];
|
||||
for (int i = 0; i < c_argc; i++)
|
||||
args[i] = fr->slots[i + 1];
|
||||
result = JS_CallInternal(ctx, fn_val, fr->slots[0], c_argc, args, 0);
|
||||
}
|
||||
|
||||
JSValue result = JS_Call(ctx, fr->function, fr->slots[0], c_argc, args);
|
||||
if (JS_IsException(result))
|
||||
return JS_EXCEPTION;
|
||||
/* Clear any stale exception left by functions that returned a valid
|
||||
value despite internal error (e.g., sign("text") returns null
|
||||
but JS_ToFloat64 leaves an exception flag) */
|
||||
if (JS_HasException(ctx))
|
||||
JS_GetException(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -549,6 +585,16 @@ JSValue cell_rt_pop(JSContext *ctx, JSValue arr) {
|
||||
|
||||
JSValue cell_rt_delete(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 = JS_NewString(ctx, name);
|
||||
int ret = JS_DeleteProperty(ctx, obj, key);
|
||||
if (ret < 0)
|
||||
return JS_EXCEPTION;
|
||||
return JS_NewBool(ctx, ret >= 0);
|
||||
}
|
||||
|
||||
@@ -595,12 +641,37 @@ JSValue cell_rt_ge_text(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return JS_NewBool(ctx, r);
|
||||
}
|
||||
|
||||
JSValue cell_rt_eq_tol(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return JS_NewBool(ctx, a == b);
|
||||
static int cell_rt_tol_eq_inner(JSContext *ctx, JSValue a, JSValue b,
|
||||
JSValue tol) {
|
||||
if (JS_IsNumber(a) && JS_IsNumber(b) && JS_IsNumber(tol)) {
|
||||
double da, db, dt;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
JS_ToFloat64(ctx, &dt, tol);
|
||||
return fabs(da - db) <= dt;
|
||||
}
|
||||
if (JS_IsText(a) && JS_IsText(b) && JS_IsBool(tol) && JS_VALUE_GET_BOOL(tol)) {
|
||||
return js_string_compare_value_nocase(ctx, a, b) == 0;
|
||||
}
|
||||
/* Fallback to standard equality */
|
||||
if (a == b) return 1;
|
||||
if (JS_IsText(a) && JS_IsText(b))
|
||||
return js_string_compare_value(ctx, a, b, 1) == 0;
|
||||
if (JS_IsNumber(a) && JS_IsNumber(b)) {
|
||||
double da, db;
|
||||
JS_ToFloat64(ctx, &da, a);
|
||||
JS_ToFloat64(ctx, &db, b);
|
||||
return da == db;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b) {
|
||||
return JS_NewBool(ctx, a != b);
|
||||
JSValue cell_rt_eq_tol(JSContext *ctx, JSValue a, JSValue b, JSValue tol) {
|
||||
return JS_NewBool(ctx, cell_rt_tol_eq_inner(ctx, a, b, tol));
|
||||
}
|
||||
|
||||
JSValue cell_rt_ne_tol(JSContext *ctx, JSValue a, JSValue b, JSValue tol) {
|
||||
return JS_NewBool(ctx, !cell_rt_tol_eq_inner(ctx, a, b, tol));
|
||||
}
|
||||
|
||||
/* --- Type check: is_proxy (function with arity 2) --- */
|
||||
@@ -612,6 +683,14 @@ int cell_rt_is_proxy(JSContext *ctx, JSValue v) {
|
||||
return fn->length == 2;
|
||||
}
|
||||
|
||||
/* --- Identity check (chases forwarding pointers) --- */
|
||||
|
||||
JSValue cell_rt_is_identical(JSContext *ctx, JSValue a, JSValue b) {
|
||||
if (JS_IsPtr(a)) a = JS_MKPTR(chase(a));
|
||||
if (JS_IsPtr(b)) b = JS_MKPTR(chase(b));
|
||||
return JS_NewBool(ctx, a == b);
|
||||
}
|
||||
|
||||
/* --- Short-circuit and/or (non-allocating) --- */
|
||||
|
||||
JSValue cell_rt_and(JSContext *ctx, JSValue left, JSValue right) {
|
||||
|
||||
Reference in New Issue
Block a user