Merge branch 'optimize_mcode' into fix_aot

This commit is contained in:
2026-02-21 20:42:25 -06:00
12 changed files with 35102 additions and 36412 deletions

View File

@@ -89,7 +89,7 @@
[P] IS_IDENTICAL, IS_INT, IS_NUM, IS_TEXT, IS_BOOL, IS_NULL
[P] IS_ARRAY, IS_FUNC, IS_RECORD, IS_STONE, IS_PROXY
[P] NOT, AND, OR, BITNOT, BITAND, BITOR, BITXOR
[P] JMP, JMPTRUE, JMPFALSE, JMPNULL, JMPNOTNULL
[P] JMP, JMPTRUE, JMPFALSE, JMPNULL, JMPNOTNULL, WARYTRUE, WARYFALSE, JMPEMPTY
[P] RETURN, RETNIL, SETARG, GETUP, SETUP, DISRUPT, THROW
[P] LENGTH (array + imm-ASCII fast path only; text/blob fallback is [G])
[N] EQ_TEXT..GE_TEXT (js_string_compare_value — no allocation)
@@ -282,6 +282,10 @@ typedef enum MachOpcode {
MACH_IS_UPPER, /* R(A) = is_upper(R(B)) */
MACH_IS_WS, /* R(A) = is_whitespace(R(B)) */
MACH_IS_ACTOR, /* R(A) = is_actor(R(B)) — has actor_sym property */
MACH_APPLY, /* R(A) = apply(R(B), R(C)) — call fn with args from array (ABC) */
MACH_WARYTRUE, /* if toBool(R(A)): pc += sBx — coercing (iAsBx) */
MACH_WARYFALSE, /* if !toBool(R(A)): pc += sBx — coercing (iAsBx) */
MACH_JMPEMPTY, /* if R(A)==empty_text: pc += sBx (iAsBx) */
MACH_OP_COUNT
} MachOpcode;
@@ -406,6 +410,10 @@ static const char *mach_opcode_names[MACH_OP_COUNT] = {
[MACH_IS_UPPER] = "is_upper",
[MACH_IS_WS] = "is_ws",
[MACH_IS_ACTOR] = "is_actor",
[MACH_APPLY] = "apply",
[MACH_WARYTRUE] = "wary_true",
[MACH_WARYFALSE] = "wary_false",
[MACH_JMPEMPTY] = "jump_empty",
};
/* ---- Compile-time constant pool entry ---- */
@@ -1427,6 +1435,8 @@ vm_dispatch:
DT(MACH_IS_DIGIT), DT(MACH_IS_LETTER),
DT(MACH_IS_LOWER), DT(MACH_IS_UPPER),
DT(MACH_IS_WS), DT(MACH_IS_ACTOR),
DT(MACH_APPLY),
DT(MACH_WARYTRUE), DT(MACH_WARYFALSE), DT(MACH_JMPEMPTY),
};
#pragma GCC diagnostic pop
#undef DT
@@ -2069,12 +2079,7 @@ vm_dispatch:
}
VM_CASE(MACH_JMPTRUE): {
JSValue v = frame->slots[a];
int cond;
if (v == JS_TRUE) cond = 1;
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
else cond = JS_ToBool(ctx, v);
if (cond) {
if (frame->slots[a] == JS_TRUE) {
int offset = MACH_GET_sBx(instr);
pc = (uint32_t)((int32_t)pc + offset);
if (offset < 0) {
@@ -2095,12 +2100,7 @@ vm_dispatch:
}
VM_CASE(MACH_JMPFALSE): {
JSValue v = frame->slots[a];
int cond;
if (v == JS_TRUE) cond = 1;
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
else cond = JS_ToBool(ctx, v);
if (!cond) {
if (frame->slots[a] == JS_FALSE) {
int offset = MACH_GET_sBx(instr);
pc = (uint32_t)((int32_t)pc + offset);
if (offset < 0) {
@@ -2517,6 +2517,49 @@ vm_dispatch:
frame->slots[a] = JS_NewBool(ctx, result);
VM_BREAK();
}
VM_CASE(MACH_APPLY): {
/* A=dest, B=fn, C=arr_or_val */
JSValue fn_val = frame->slots[b];
JSValue arg_val = frame->slots[c];
if (!mist_is_function(fn_val)) {
frame->slots[a] = fn_val;
VM_BREAK();
}
JSFunction *fn = JS_VALUE_GET_FUNCTION(fn_val);
JSValue ret;
ctx->reg_current_frame = frame_ref.val;
ctx->current_register_pc = pc > 0 ? pc - 1 : 0;
ctx->vm_call_depth++;
if (!mist_is_array(arg_val)) {
/* Non-array: use as single argument */
if (!mach_check_call_arity(ctx, fn, 1)) {
ctx->vm_call_depth--;
goto disrupt;
}
ret = JS_CallInternal(ctx, fn_val, JS_NULL, 1, &arg_val, 0);
} else {
JSArray *arr = JS_VALUE_GET_ARRAY(arg_val);
int len = arr->len;
if (!mach_check_call_arity(ctx, fn, len)) {
ctx->vm_call_depth--;
goto disrupt;
}
if (len == 0) {
ret = JS_CallInternal(ctx, fn_val, JS_NULL, 0, NULL, 0);
} else {
JSValue *args = alloca(sizeof(JSValue) * len);
for (int i = 0; i < len; i++)
args[i] = arr->values[i];
ret = JS_CallInternal(ctx, fn_val, JS_NULL, len, args, 0);
}
}
ctx->vm_call_depth--;
frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val);
ctx->reg_current_frame = JS_NULL;
if (JS_IsException(ret)) goto disrupt;
frame->slots[a] = ret;
VM_BREAK();
}
/* Logical */
VM_CASE(MACH_NOT): {
int bval = JS_ToBool(ctx, frame->slots[b]);
@@ -2826,6 +2869,67 @@ vm_dispatch:
VM_BREAK();
}
/* Wary jumps — coerce via JS_ToBool (old JMPTRUE/JMPFALSE behavior) */
VM_CASE(MACH_WARYTRUE): {
JSValue v = frame->slots[a];
int cond;
if (v == JS_TRUE) cond = 1;
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
else cond = JS_ToBool(ctx, v);
if (cond) {
int offset = MACH_GET_sBx(instr);
pc = (uint32_t)((int32_t)pc + offset);
if (offset < 0) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf == 2) {
result = JS_RaiseDisrupt(ctx, "interrupted");
goto done;
}
if (pf == 1) {
if (ctx->vm_call_depth > 0)
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
else
goto suspend;
}
}
}
VM_BREAK();
}
VM_CASE(MACH_WARYFALSE): {
JSValue v = frame->slots[a];
int cond;
if (v == JS_TRUE) cond = 1;
else if (v == JS_FALSE || v == JS_NULL) cond = 0;
else cond = JS_ToBool(ctx, v);
if (!cond) {
int offset = MACH_GET_sBx(instr);
pc = (uint32_t)((int32_t)pc + offset);
if (offset < 0) {
int pf = atomic_load_explicit(&ctx->pause_flag, memory_order_relaxed);
if (pf == 2) {
result = JS_RaiseDisrupt(ctx, "interrupted");
goto done;
}
if (pf == 1) {
if (ctx->vm_call_depth > 0)
atomic_store_explicit(&ctx->pause_flag, 0, memory_order_relaxed);
else
goto suspend;
}
}
}
VM_BREAK();
}
VM_CASE(MACH_JMPEMPTY): {
if (frame->slots[a] == JS_EMPTY_TEXT) {
int offset = MACH_GET_sBx(instr);
pc = (uint32_t)((int32_t)pc + offset);
}
VM_BREAK();
}
/* Disrupt (mcode alias) */
VM_CASE(MACH_DISRUPT):
goto disrupt;
@@ -3174,6 +3278,7 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
else if (strcmp(op, "is_upper") == 0) { AB2(MACH_IS_UPPER); }
else if (strcmp(op, "is_ws") == 0) { AB2(MACH_IS_WS); }
else if (strcmp(op, "is_actor") == 0) { AB2(MACH_IS_ACTOR); }
else if (strcmp(op, "apply") == 0) { ABC3(MACH_APPLY); }
/* Logical */
else if (strcmp(op, "not") == 0) { AB2(MACH_NOT); }
else if (strcmp(op, "and") == 0) { ABC3(MACH_AND); }
@@ -3324,6 +3429,34 @@ static MachCode *mcode_lower_func(cJSON *fobj, const char *filename) {
EM(MACH_AsBx(MACH_JMPNOTNULL, reg, 0));
ml_patch(&s, pc_now, lbl, 0, reg);
}
else if (strcmp(op, "jump_null") == 0) {
int reg = A1;
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
int pc_now = s.code_count;
EM(MACH_AsBx(MACH_JMPNULL, reg, 0));
ml_patch(&s, pc_now, lbl, 0, reg);
}
else if (strcmp(op, "wary_true") == 0) {
int reg = A1;
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
int pc_now = s.code_count;
EM(MACH_AsBx(MACH_WARYTRUE, reg, 0));
ml_patch(&s, pc_now, lbl, 0, reg);
}
else if (strcmp(op, "wary_false") == 0) {
int reg = A1;
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
int pc_now = s.code_count;
EM(MACH_AsBx(MACH_WARYFALSE, reg, 0));
ml_patch(&s, pc_now, lbl, 0, reg);
}
else if (strcmp(op, "jump_empty") == 0) {
int reg = A1;
const char *lbl = cJSON_GetArrayItem(it, 2)->valuestring;
int pc_now = s.code_count;
EM(MACH_AsBx(MACH_JMPEMPTY, reg, 0));
ml_patch(&s, pc_now, lbl, 0, reg);
}
/* Return / error */
else if (strcmp(op, "return") == 0) {
EM(MACH_ABC(MACH_RETURN, A1, 0, 0));

View File

@@ -9446,15 +9446,23 @@ static JSValue js_cell_fn_apply (JSContext *ctx, JSValue this_val, int argc, JSV
if (argc < 1) return JS_NULL;
if (!JS_IsFunction (argv[0])) return argv[0];
JSFunction *fn = JS_VALUE_GET_FUNCTION (argv[0]);
if (argc < 2)
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);
if (!JS_IsArray (argv[1]))
if (!JS_IsArray (argv[1])) {
if (fn->length >= 0 && 1 > fn->length)
return JS_RaiseDisrupt (ctx, "too many arguments for apply: expected %d, got 1", fn->length);
return JS_CallInternal (ctx, argv[0], JS_NULL, 1, &argv[1], 0);
}
JSArray *arr = JS_VALUE_GET_ARRAY (argv[1]);
int len = arr->len;
if (fn->length >= 0 && len > fn->length)
return JS_RaiseDisrupt (ctx, "too many arguments for apply: expected %d, got %d", fn->length, len);
if (len == 0)
return JS_CallInternal (ctx, argv[0], JS_NULL, 0, NULL, 0);