From 23b201bdd70db3f3ddffbbabe166120b360daa66 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 15 Feb 2026 17:51:07 -0600 Subject: [PATCH] dynamic dispatch --- source/mach.c | 732 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 522 insertions(+), 210 deletions(-) diff --git a/source/mach.c b/source/mach.c index 2e0dae6b..8abbe712 100644 --- a/source/mach.c +++ b/source/mach.c @@ -438,7 +438,7 @@ static void js_free_code_register(JSCodeRegister *code) { /* Allocate a JSFrameRegister on the GC heap */ JSFrameRegister *alloc_frame_register(JSContext *ctx, int slot_count) { size_t size = sizeof(JSFrameRegister) + slot_count * sizeof(JSValue); - JSFrameRegister *frame = js_mallocz(ctx, size); + JSFrameRegister *frame = js_malloc(ctx, size); if (!frame) return NULL; /* cap56 = slot count (used by gc_object_size) */ @@ -786,88 +786,377 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, /* Execution loop — 32-bit instruction dispatch */ for (;;) { - if (reg_vm_check_interrupt(ctx)) { - result = JS_ThrowInternalError(ctx, "interrupted"); +#ifndef NDEBUG + if (pc >= code->instr_count) { + fprintf(stderr, "mach VM: pc %u overran code->instr_count %u (missing RETURN/RETNIL?)\n", + pc, code->instr_count); + result = JS_ThrowInternalError(ctx, "pc overrun"); goto done; } +#endif - if (pc >= code->instr_count) { - /* End of code — implicit return null */ - result = JS_NULL; - if (JS_IsNull(frame->caller)) goto done; + MachInstr32 instr; + int op, a, b, c; - /* Pop frame */ - JSFrameRegister *caller = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->caller); - frame->caller = JS_NULL; - frame = caller; - frame_ref.val = JS_MKPTR(frame); - int ret_info = JS_VALUE_GET_INT(frame->address); - JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); - code = fn->u.reg.code; - env = fn->u.reg.env_record; - pc = ret_info >> 16; - int ret_slot = ret_info & 0xFFFF; - if (ret_slot != 0xFFFF) frame->slots[ret_slot] = result; - continue; - } - - MachInstr32 instr = code->instructions[pc++]; - ctx->reg_current_frame = frame_ref.val; - ctx->current_register_pc = pc > 0 ? pc - 1 : 0; - int op = MACH_GET_OP(instr); - int a = MACH_GET_A(instr); - int b = MACH_GET_B(instr); - int c = MACH_GET_C(instr); +#ifdef __GNUC__ + /* Computed goto dispatch — each opcode gets its own branch predictor entry */ + /* Use a macro to generate consistent dispatch table entries and labels */ +#define DT(x) [x] = &&op_##x + static const void *dispatch_table[256] = { + [0 ... 255] = &&op_DEFAULT, + DT(MACH_LOADK), DT(MACH_LOADI), + DT(MACH_LOADNULL), DT(MACH_LOADTRUE), + DT(MACH_LOADFALSE), DT(MACH_MOVE), + DT(MACH_ADD), DT(MACH_SUB), + DT(MACH_MUL), DT(MACH_DIV), + DT(MACH_MOD), DT(MACH_POW), + DT(MACH_NEG), DT(MACH_INC), DT(MACH_DEC), + DT(MACH_EQ), DT(MACH_NEQ), + DT(MACH_LT), DT(MACH_LE), + DT(MACH_GT), DT(MACH_GE), + DT(MACH_LNOT), DT(MACH_BNOT), + DT(MACH_BAND), DT(MACH_BOR), + DT(MACH_BXOR), DT(MACH_SHL), + DT(MACH_SHR), DT(MACH_USHR), + DT(MACH_GETFIELD), DT(MACH_SETFIELD), + DT(MACH_GETINDEX), DT(MACH_SETINDEX), + DT(MACH_GETNAME), DT(MACH_GETINTRINSIC), + DT(MACH_GETENV), + DT(MACH_GETUP), DT(MACH_SETUP), + DT(MACH_JMP), DT(MACH_JMPTRUE), + DT(MACH_JMPFALSE), DT(MACH_JMPNULL), + DT(MACH_RETURN), DT(MACH_RETNIL), + DT(MACH_NEWOBJECT), DT(MACH_NEWARRAY), + DT(MACH_CLOSURE), + DT(MACH_THROW), DT(MACH_PUSH), DT(MACH_POP), + DT(MACH_DELETE), DT(MACH_DELETEINDEX), + DT(MACH_HASPROP), DT(MACH_REGEXP), + DT(MACH_EQ_TOL), DT(MACH_NEQ_TOL), + DT(MACH_NOP), + DT(MACH_CONCAT), + DT(MACH_EQ_INT), DT(MACH_NE_INT), + DT(MACH_LT_INT), DT(MACH_LE_INT), + DT(MACH_GT_INT), DT(MACH_GE_INT), + DT(MACH_EQ_FLOAT), DT(MACH_NE_FLOAT), + DT(MACH_LT_FLOAT), DT(MACH_LE_FLOAT), + DT(MACH_GT_FLOAT), DT(MACH_GE_FLOAT), + DT(MACH_EQ_TEXT), DT(MACH_NE_TEXT), + DT(MACH_LT_TEXT), DT(MACH_LE_TEXT), + DT(MACH_GT_TEXT), DT(MACH_GE_TEXT), + DT(MACH_EQ_BOOL), DT(MACH_NE_BOOL), + DT(MACH_IS_IDENTICAL), + DT(MACH_IS_INT), DT(MACH_IS_NUM), + DT(MACH_IS_TEXT), DT(MACH_IS_BOOL), + DT(MACH_IS_NULL), + DT(MACH_NOT), DT(MACH_AND), DT(MACH_OR), + DT(MACH_BITNOT), DT(MACH_BITAND), + DT(MACH_BITOR), DT(MACH_BITXOR), + DT(MACH_LOAD_FIELD), DT(MACH_STORE_FIELD), + DT(MACH_LOAD_INDEX), DT(MACH_STORE_INDEX), + DT(MACH_LOAD_DYNAMIC), DT(MACH_STORE_DYNAMIC), + DT(MACH_NEWRECORD), + DT(MACH_FRAME), DT(MACH_SETARG), + DT(MACH_INVOKE), DT(MACH_GOFRAME), + DT(MACH_GOINVOKE), + DT(MACH_JMPNOTNULL), + DT(MACH_DISRUPT), + DT(MACH_SET_VAR), DT(MACH_IN), + DT(MACH_IS_ARRAY), DT(MACH_IS_FUNC), + DT(MACH_IS_RECORD), DT(MACH_IS_STONE), + DT(MACH_LENGTH), DT(MACH_IS_PROXY), + }; +#undef DT +#define VM_DECODE() do { \ + instr = code->instructions[pc++]; \ + op = MACH_GET_OP(instr); \ + a = MACH_GET_A(instr); \ + b = MACH_GET_B(instr); \ + c = MACH_GET_C(instr); \ + } while(0) +#define VM_DISPATCH() do { VM_DECODE(); goto *dispatch_table[op]; } while(0) +/* VM_CASE: dual-label — goto target for computed goto + case for switch fallback */ +#define VM_CASE(x) op_##x: case x +#define VM_DEFAULT op_DEFAULT: default +#define VM_BREAK() VM_DISPATCH() +#else +#define VM_DECODE() do { \ + instr = code->instructions[pc++]; \ + op = MACH_GET_OP(instr); \ + a = MACH_GET_A(instr); \ + b = MACH_GET_B(instr); \ + c = MACH_GET_C(instr); \ + } while(0) +#define VM_DISPATCH() do { VM_DECODE(); } while(0) +#define VM_CASE(x) case x +#define VM_DEFAULT default +#define VM_BREAK() break +#endif + VM_DECODE(); +#ifdef __GNUC__ + goto *dispatch_table[op]; +#endif switch (op) { - case MACH_NOP: - break; + VM_CASE(MACH_NOP): + VM_BREAK(); - case MACH_LOADK: { + VM_CASE(MACH_LOADK): { int bx = MACH_GET_Bx(instr); if (bx < (int)code->cpool_count) frame->slots[a] = code->cpool[bx]; - break; + VM_BREAK(); } - case MACH_LOADI: + VM_CASE(MACH_LOADI): frame->slots[a] = JS_NewInt32(ctx, MACH_GET_sBx(instr)); - break; + VM_BREAK(); - case MACH_LOADNULL: + VM_CASE(MACH_LOADNULL): frame->slots[a] = JS_NULL; - break; + VM_BREAK(); - case MACH_LOADTRUE: + VM_CASE(MACH_LOADTRUE): frame->slots[a] = JS_TRUE; - break; + VM_BREAK(); - case MACH_LOADFALSE: + VM_CASE(MACH_LOADFALSE): frame->slots[a] = JS_FALSE; - break; + VM_BREAK(); - case MACH_MOVE: + VM_CASE(MACH_MOVE): frame->slots[a] = frame->slots[b]; - break; + VM_BREAK(); - /* Arithmetic / comparison / bitwise — all ABC format */ - case MACH_ADD: case MACH_SUB: case MACH_MUL: case MACH_DIV: - case MACH_MOD: case MACH_POW: - case MACH_EQ: case MACH_NEQ: case MACH_LT: case MACH_LE: - case MACH_GT: case MACH_GE: - case MACH_BAND: case MACH_BOR: case MACH_BXOR: - case MACH_SHL: case MACH_SHR: case MACH_USHR: { - JSValue left = frame->slots[b]; - JSValue right = frame->slots[c]; - JSValue res = reg_vm_binop(ctx, op, left, right); + /* Arithmetic — inline integer fast paths, slow path calls reg_vm_binop */ + VM_CASE(MACH_ADD): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) + (int64_t)JS_VALUE_GET_INT(right); + frame->slots[a] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else { + JSValue res = reg_vm_binop(ctx, MACH_ADD, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_SUB): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) - (int64_t)JS_VALUE_GET_INT(right); + frame->slots[a] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else { + JSValue res = reg_vm_binop(ctx, MACH_SUB, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_MUL): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int64_t r = (int64_t)JS_VALUE_GET_INT(left) * (int64_t)JS_VALUE_GET_INT(right); + frame->slots[a] = (r >= INT32_MIN && r <= INT32_MAX) ? JS_NewInt32(ctx, (int32_t)r) : JS_NewFloat64(ctx, (double)r); + } else { + JSValue res = reg_vm_binop(ctx, MACH_MUL, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_DIV): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ia = JS_VALUE_GET_INT(left), ib = JS_VALUE_GET_INT(right); + if (ib != 0 && ia % ib == 0) + frame->slots[a] = JS_NewInt32(ctx, ia / ib); + else if (ib != 0) + frame->slots[a] = JS_NewFloat64(ctx, (double)ia / (double)ib); + else + frame->slots[a] = JS_NULL; + } else { + JSValue res = reg_vm_binop(ctx, MACH_DIV, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_MOD): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + int32_t ib = JS_VALUE_GET_INT(right); + frame->slots[a] = (ib != 0) ? JS_NewInt32(ctx, JS_VALUE_GET_INT(left) % ib) : JS_NULL; + } else { + JSValue res = reg_vm_binop(ctx, MACH_MOD, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_POW): { + JSValue left = frame->slots[b], right = frame->slots[c]; + JSValue res = reg_vm_binop(ctx, MACH_POW, left, right); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - if (JS_IsException(res)) { goto disrupt; } + if (JS_IsException(res)) goto disrupt; frame->slots[a] = res; - break; + VM_BREAK(); } - case MACH_EQ_TOL: - case MACH_NEQ_TOL: { + /* Comparison — inline integer fast paths */ + VM_CASE(MACH_EQ): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) == JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_EQ, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_NEQ): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) != JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_NEQ, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_LT): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) < JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_LT, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_LE): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) <= JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_LE, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_GT): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) > JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_GT, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_GE): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(left) >= JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_GE, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + + /* Bitwise — inline integer fast paths */ + VM_CASE(MACH_BAND): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) & JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_BAND, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_BOR): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) | JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_BOR, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_BXOR): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) ^ JS_VALUE_GET_INT(right)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_BXOR, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_SHL): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) << (JS_VALUE_GET_INT(right) & 31)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_SHL, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_SHR): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, JS_VALUE_GET_INT(left) >> (JS_VALUE_GET_INT(right) & 31)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_SHR, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + VM_CASE(MACH_USHR): { + JSValue left = frame->slots[b], right = frame->slots[c]; + if (JS_VALUE_IS_BOTH_INT(left, right)) { + frame->slots[a] = JS_NewInt32(ctx, (uint32_t)JS_VALUE_GET_INT(left) >> (JS_VALUE_GET_INT(right) & 31)); + } else { + JSValue res = reg_vm_binop(ctx, MACH_USHR, left, right); + frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); + if (JS_IsException(res)) goto disrupt; + frame->slots[a] = res; + } + VM_BREAK(); + } + + VM_CASE(MACH_EQ_TOL): + VM_CASE(MACH_NEQ_TOL): { /* A=dest, B=base, C=3; args in R(B), R(B+1), R(B+2) */ JSValue left = frame->slots[b]; JSValue right = frame->slots[b + 1]; @@ -890,10 +1179,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, if (JS_IsException(res)) { goto disrupt; } frame->slots[a] = res; } - break; + VM_BREAK(); } - case MACH_NEG: { + VM_CASE(MACH_NEG): { JSValue v = frame->slots[b]; if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); @@ -906,10 +1195,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JS_ToFloat64(ctx, &d, v); frame->slots[a] = JS_NewFloat64(ctx, -d); } - break; + VM_BREAK(); } - case MACH_INC: { + VM_CASE(MACH_INC): { JSValue v = frame->slots[b]; if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); @@ -922,10 +1211,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JS_ToFloat64(ctx, &d, v); frame->slots[a] = JS_NewFloat64(ctx, d + 1); } - break; + VM_BREAK(); } - case MACH_DEC: { + VM_CASE(MACH_DEC): { JSValue v = frame->slots[b]; if (JS_IsInt(v)) { int32_t i = JS_VALUE_GET_INT(v); @@ -938,23 +1227,23 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JS_ToFloat64(ctx, &d, v); frame->slots[a] = JS_NewFloat64(ctx, d - 1); } - break; + VM_BREAK(); } - case MACH_LNOT: { + VM_CASE(MACH_LNOT): { int bval = JS_ToBool(ctx, frame->slots[b]); frame->slots[a] = JS_NewBool(ctx, !bval); - break; + VM_BREAK(); } - case MACH_BNOT: { + VM_CASE(MACH_BNOT): { int32_t i; JS_ToInt32(ctx, &i, frame->slots[b]); frame->slots[a] = JS_NewInt32(ctx, ~i); - break; + VM_BREAK(); } - case MACH_GETFIELD: { + VM_CASE(MACH_GETFIELD): { JSValue obj = frame->slots[b]; JSValue key = code->cpool[c]; /* Non-proxy functions (arity != 2) can't have properties read */ @@ -970,10 +1259,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_SETFIELD: { + VM_CASE(MACH_SETFIELD): { /* R(A)[K(B)] = R(C) */ JSValue obj = frame->slots[a]; JSValue key = code->cpool[b]; @@ -982,20 +1271,20 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (ret < 0) goto disrupt; mach_resolve_forward(&frame->slots[a]); - break; + VM_BREAK(); } - case MACH_GETINDEX: { + VM_CASE(MACH_GETINDEX): { /* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */ JSValue obj = frame->slots[b]; JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c])); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_SETINDEX: { + VM_CASE(MACH_SETINDEX): { /* R(A)[R(B)] = R(C) — mcode guarantees R(B) is int */ JSValue obj = frame->slots[a]; JSValue val = frame->slots[c]; @@ -1003,10 +1292,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(r)) goto disrupt; mach_resolve_forward(&frame->slots[a]); - break; + VM_BREAK(); } - case MACH_GETINTRINSIC: { + VM_CASE(MACH_GETINTRINSIC): { int bx = MACH_GET_Bx(instr); JSValue key = code->cpool[bx]; JSValue val = JS_GetProperty(ctx, ctx->global_obj, key); @@ -1022,10 +1311,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, } } frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_GETENV: { + VM_CASE(MACH_GETENV): { /* Read env fresh from frame->function — C local env can go stale after GC */ int bx = MACH_GET_Bx(instr); JSValue key = code->cpool[bx]; @@ -1033,10 +1322,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JSValue val = JS_GetProperty(ctx, cur_env, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_GETNAME: { + VM_CASE(MACH_GETNAME): { /* Runtime fallback: try env then global (should not appear in linked code) */ int bx = MACH_GET_Bx(instr); JSValue key = code->cpool[bx]; @@ -1051,10 +1340,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); } frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_GETUP: { + VM_CASE(MACH_GETUP): { /* R(A) = outer_frame[B].slots[C] — walk lexical scope chain */ int depth = b; JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); @@ -1077,10 +1366,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, target = next; } frame->slots[a] = target->slots[c]; - break; + VM_BREAK(); } - case MACH_SETUP: { + VM_CASE(MACH_SETUP): { /* outer_frame[B].slots[C] = R(A) — walk lexical scope chain */ int depth = b; JSFunction *fn = JS_VALUE_GET_FUNCTION(frame->function); @@ -1090,42 +1379,62 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, target = (JSFrameRegister *)JS_VALUE_GET_PTR(fn->u.reg.outer_frame); } target->slots[c] = frame->slots[a]; - break; + VM_BREAK(); } - case MACH_JMP: { + VM_CASE(MACH_JMP): { int offset = MACH_GET_sJ(instr); pc = (uint32_t)((int32_t)pc + offset); - break; + if (offset < 0 && reg_vm_check_interrupt(ctx)) { + result = JS_ThrowInternalError(ctx, "interrupted"); + goto done; + } + VM_BREAK(); } - case MACH_JMPTRUE: { - int cond = JS_ToBool(ctx, frame->slots[a]); + 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) { int offset = MACH_GET_sBx(instr); pc = (uint32_t)((int32_t)pc + offset); + if (offset < 0 && reg_vm_check_interrupt(ctx)) { + result = JS_ThrowInternalError(ctx, "interrupted"); + goto done; + } } - break; + VM_BREAK(); } - case MACH_JMPFALSE: { - int cond = JS_ToBool(ctx, frame->slots[a]); + 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) { int offset = MACH_GET_sBx(instr); pc = (uint32_t)((int32_t)pc + offset); + if (offset < 0 && reg_vm_check_interrupt(ctx)) { + result = JS_ThrowInternalError(ctx, "interrupted"); + goto done; + } } - break; + VM_BREAK(); } - case MACH_JMPNULL: { + VM_CASE(MACH_JMPNULL): { if (JS_IsNull(frame->slots[a])) { int offset = MACH_GET_sBx(instr); pc = (uint32_t)((int32_t)pc + offset); } - break; + VM_BREAK(); } - case MACH_RETURN: + VM_CASE(MACH_RETURN): result = frame->slots[a]; if (JS_IsNull(frame->caller)) goto done; { @@ -1167,9 +1476,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame->slots[ret_slot] = result; } } - break; + VM_BREAK(); - case MACH_RETNIL: + VM_CASE(MACH_RETNIL): result = JS_NULL; if (JS_IsNull(frame->caller)) goto done; { @@ -1185,25 +1494,25 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, int ret_slot = ret_info & 0xFFFF; if (ret_slot != 0xFFFF) frame->slots[ret_slot] = result; } - break; + VM_BREAK(); - case MACH_NEWOBJECT: { + VM_CASE(MACH_NEWOBJECT): { JSValue obj = JS_NewObject(ctx); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(obj)) { goto disrupt; } frame->slots[a] = obj; - break; + VM_BREAK(); } - case MACH_NEWARRAY: { + VM_CASE(MACH_NEWARRAY): { JSValue arr = JS_NewArrayCap(ctx, b); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(arr)) { goto disrupt; } frame->slots[a] = arr; - break; + VM_BREAK(); } - case MACH_CLOSURE: { + VM_CASE(MACH_CLOSURE): { int bx = MACH_GET_Bx(instr); if ((uint32_t)bx < code->func_count) { JSCodeRegister *fn_code = code->functions[bx]; @@ -1215,10 +1524,10 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, } else { frame->slots[a] = JS_NULL; } - break; + VM_BREAK(); } - case MACH_PUSH: { + VM_CASE(MACH_PUSH): { /* push R(B) onto array R(A) — mcode guarantees R(A) is array */ JSValue arr = frame->slots[a]; JSValue val = frame->slots[b]; @@ -1230,49 +1539,49 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JS_PopGCRef(ctx, &arr_gc); if (rc < 0) goto disrupt; if (arr_gc.val != arr) frame->slots[a] = arr_gc.val; - break; + VM_BREAK(); } - case MACH_POP: { + VM_CASE(MACH_POP): { /* R(A) = pop last element from array R(B) — mcode guarantees R(B) is array */ JSValue arr = frame->slots[b]; JSValue val = JS_ArrayPop(ctx, arr); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_DELETE: { + VM_CASE(MACH_DELETE): { JSValue obj = frame->slots[b]; JSValue key = code->cpool[c]; int ret = JS_DeleteProperty(ctx, obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (ret < 0) goto disrupt; frame->slots[a] = JS_NewBool(ctx, ret >= 0); - break; + VM_BREAK(); } - case MACH_DELETEINDEX: { + VM_CASE(MACH_DELETEINDEX): { JSValue obj = frame->slots[b]; JSValue key = frame->slots[c]; int ret = JS_DeleteProperty(ctx, obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (ret < 0) goto disrupt; frame->slots[a] = JS_NewBool(ctx, ret >= 0); - break; + VM_BREAK(); } - case MACH_HASPROP: { + VM_CASE(MACH_HASPROP): { JSValue obj = frame->slots[b]; JSValue key = frame->slots[c]; int has = JS_HasProperty(ctx, obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[a] = JS_NewBool(ctx, has > 0); - break; + VM_BREAK(); } - case MACH_REGEXP: { + VM_CASE(MACH_REGEXP): { JSValue argv[2]; argv[0] = code->cpool[b]; /* pattern */ argv[1] = code->cpool[c]; /* flags */ @@ -1280,47 +1589,47 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(re)) goto disrupt; frame->slots[a] = re; - break; + VM_BREAK(); } - case MACH_THROW: + VM_CASE(MACH_THROW): goto disrupt; /* === New mcode-derived opcodes === */ /* Text concatenation */ - case MACH_CONCAT: { + VM_CASE(MACH_CONCAT): { JSValue res = JS_ConcatString(ctx, frame->slots[b], frame->slots[c]); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(res)) goto disrupt; frame->slots[a] = res; - break; + VM_BREAK(); } /* Typed integer comparisons */ - case MACH_EQ_INT: + VM_CASE(MACH_EQ_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) == JS_VALUE_GET_INT(frame->slots[c])); - break; - case MACH_NE_INT: + VM_BREAK(); + VM_CASE(MACH_NE_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) != JS_VALUE_GET_INT(frame->slots[c])); - break; - case MACH_LT_INT: + VM_BREAK(); + VM_CASE(MACH_LT_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) < JS_VALUE_GET_INT(frame->slots[c])); - break; - case MACH_LE_INT: + VM_BREAK(); + VM_CASE(MACH_LE_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) <= JS_VALUE_GET_INT(frame->slots[c])); - break; - case MACH_GT_INT: + VM_BREAK(); + VM_CASE(MACH_GT_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) > JS_VALUE_GET_INT(frame->slots[c])); - break; - case MACH_GE_INT: + VM_BREAK(); + VM_CASE(MACH_GE_INT): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_INT(frame->slots[b]) >= JS_VALUE_GET_INT(frame->slots[c])); - break; + VM_BREAK(); /* Typed float comparisons */ - case MACH_EQ_FLOAT: case MACH_NE_FLOAT: - case MACH_LT_FLOAT: case MACH_LE_FLOAT: - case MACH_GT_FLOAT: case MACH_GE_FLOAT: { + VM_CASE(MACH_EQ_FLOAT): VM_CASE(MACH_NE_FLOAT): + VM_CASE(MACH_LT_FLOAT): VM_CASE(MACH_LE_FLOAT): + VM_CASE(MACH_GT_FLOAT): VM_CASE(MACH_GE_FLOAT): { double da, db; JS_ToFloat64(ctx, &da, frame->slots[b]); JS_ToFloat64(ctx, &db, frame->slots[c]); @@ -1335,13 +1644,13 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, default: r = 0; break; } frame->slots[a] = JS_NewBool(ctx, r); - break; + VM_BREAK(); } /* Typed text comparisons */ - case MACH_EQ_TEXT: case MACH_NE_TEXT: - case MACH_LT_TEXT: case MACH_LE_TEXT: - case MACH_GT_TEXT: case MACH_GE_TEXT: { + VM_CASE(MACH_EQ_TEXT): VM_CASE(MACH_NE_TEXT): + VM_CASE(MACH_LT_TEXT): VM_CASE(MACH_LE_TEXT): + VM_CASE(MACH_GT_TEXT): VM_CASE(MACH_GE_TEXT): { int cmp = js_string_compare_value(ctx, frame->slots[b], frame->slots[c], FALSE); int r; switch (op) { @@ -1354,54 +1663,54 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, default: r = 0; break; } frame->slots[a] = JS_NewBool(ctx, r); - break; + VM_BREAK(); } /* Typed bool comparisons */ - case MACH_EQ_BOOL: + VM_CASE(MACH_EQ_BOOL): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) == JS_VALUE_GET_BOOL(frame->slots[c])); - break; - case MACH_NE_BOOL: + VM_BREAK(); + VM_CASE(MACH_NE_BOOL): frame->slots[a] = JS_NewBool(ctx, JS_VALUE_GET_BOOL(frame->slots[b]) != JS_VALUE_GET_BOOL(frame->slots[c])); - break; + VM_BREAK(); /* Identity check */ - case MACH_IS_IDENTICAL: { + VM_CASE(MACH_IS_IDENTICAL): { JSValue va = JS_IsPtr(frame->slots[b]) ? JS_MKPTR(chase(frame->slots[b])) : frame->slots[b]; JSValue vb = JS_IsPtr(frame->slots[c]) ? JS_MKPTR(chase(frame->slots[c])) : frame->slots[c]; frame->slots[a] = JS_NewBool(ctx, va == vb); - break; + VM_BREAK(); } /* Type checks */ - case MACH_IS_INT: + VM_CASE(MACH_IS_INT): frame->slots[a] = JS_NewBool(ctx, JS_IsInt(frame->slots[b])); - break; - case MACH_IS_NUM: + VM_BREAK(); + VM_CASE(MACH_IS_NUM): frame->slots[a] = JS_NewBool(ctx, JS_IsNumber(frame->slots[b])); - break; - case MACH_IS_TEXT: + VM_BREAK(); + VM_CASE(MACH_IS_TEXT): frame->slots[a] = JS_NewBool(ctx, mist_is_text(frame->slots[b])); - break; - case MACH_IS_BOOL: + VM_BREAK(); + VM_CASE(MACH_IS_BOOL): frame->slots[a] = JS_NewBool(ctx, JS_IsBool(frame->slots[b])); - break; - case MACH_IS_NULL: + VM_BREAK(); + VM_CASE(MACH_IS_NULL): frame->slots[a] = JS_NewBool(ctx, JS_IsNull(frame->slots[b])); - break; - case MACH_IS_ARRAY: + VM_BREAK(); + VM_CASE(MACH_IS_ARRAY): frame->slots[a] = JS_NewBool(ctx, mist_is_array(frame->slots[b])); - break; - case MACH_IS_FUNC: + VM_BREAK(); + VM_CASE(MACH_IS_FUNC): frame->slots[a] = JS_NewBool(ctx, mist_is_function(frame->slots[b])); - break; - case MACH_IS_RECORD: + VM_BREAK(); + VM_CASE(MACH_IS_RECORD): frame->slots[a] = JS_NewBool(ctx, mist_is_record(frame->slots[b])); - break; - case MACH_IS_STONE: + VM_BREAK(); + VM_CASE(MACH_IS_STONE): frame->slots[a] = JS_NewBool(ctx, mist_is_stone(frame->slots[b])); - break; - case MACH_LENGTH: { + VM_BREAK(); + VM_CASE(MACH_LENGTH): { JSValue v = frame->slots[b]; if (mist_is_array(v)) { JSArray *arr = JS_VALUE_GET_ARRAY(v); @@ -1414,9 +1723,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[a] = res; } - break; + VM_BREAK(); } - case MACH_IS_PROXY: { + VM_CASE(MACH_IS_PROXY): { JSValue v = frame->slots[b]; int is_proxy = 0; if (mist_is_function(v)) { @@ -1424,39 +1733,39 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, is_proxy = (fn->length == 2); } frame->slots[a] = JS_NewBool(ctx, is_proxy); - break; + VM_BREAK(); } /* Logical */ - case MACH_NOT: { + VM_CASE(MACH_NOT): { int bval = JS_ToBool(ctx, frame->slots[b]); frame->slots[a] = JS_NewBool(ctx, !bval); - break; + VM_BREAK(); } - case MACH_AND: { + VM_CASE(MACH_AND): { JSValue left = frame->slots[b]; if (!JS_ToBool(ctx, left)) frame->slots[a] = left; else frame->slots[a] = frame->slots[c]; - break; + VM_BREAK(); } - case MACH_OR: { + VM_CASE(MACH_OR): { JSValue left = frame->slots[b]; if (JS_ToBool(ctx, left)) frame->slots[a] = left; else frame->slots[a] = frame->slots[c]; - break; + VM_BREAK(); } /* Bitwise (mcode names — delegate to same code as legacy) */ - case MACH_BITNOT: { + VM_CASE(MACH_BITNOT): { int32_t i; JS_ToInt32(ctx, &i, frame->slots[b]); frame->slots[a] = JS_NewInt32(ctx, ~i); - break; + VM_BREAK(); } - case MACH_BITAND: case MACH_BITOR: case MACH_BITXOR: { + VM_CASE(MACH_BITAND): VM_CASE(MACH_BITOR): VM_CASE(MACH_BITXOR): { int32_t ia, ib; JS_ToInt32(ctx, &ia, frame->slots[b]); JS_ToInt32(ctx, &ib, frame->slots[c]); @@ -1465,11 +1774,11 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, else if (op == MACH_BITOR) r = ia | ib; else r = ia ^ ib; frame->slots[a] = JS_NewInt32(ctx, r); - break; + VM_BREAK(); } /* Property access (mcode names) */ - case MACH_LOAD_FIELD: { + VM_CASE(MACH_LOAD_FIELD): { JSValue obj = frame->slots[b]; JSValue key = code->cpool[c]; if (mist_is_function(obj)) { @@ -1481,9 +1790,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_STORE_FIELD: { + VM_CASE(MACH_STORE_FIELD): { JSValue obj = frame->slots[a]; JSValue key = code->cpool[b]; JSValue val = frame->slots[c]; @@ -1491,18 +1800,18 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (ret < 0) goto disrupt; mach_resolve_forward(&frame->slots[a]); - break; + VM_BREAK(); } - case MACH_LOAD_INDEX: { + VM_CASE(MACH_LOAD_INDEX): { /* R(A) = R(B)[R(C)] — mcode guarantees R(C) is int */ JSValue obj = frame->slots[b]; JSValue val = JS_GetPropertyNumber(ctx, obj, JS_VALUE_GET_INT(frame->slots[c])); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_STORE_INDEX: { + VM_CASE(MACH_STORE_INDEX): { /* R(A)[R(B)] = R(C) — mcode guarantees R(B) is int */ JSValue obj = frame->slots[a]; JSValue val = frame->slots[c]; @@ -1510,9 +1819,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(r)) goto disrupt; mach_resolve_forward(&frame->slots[a]); - break; + VM_BREAK(); } - case MACH_LOAD_DYNAMIC: { + VM_CASE(MACH_LOAD_DYNAMIC): { JSValue obj = frame->slots[b]; JSValue key = frame->slots[c]; JSValue val; @@ -1523,9 +1832,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(val)) goto disrupt; frame->slots[a] = val; - break; + VM_BREAK(); } - case MACH_STORE_DYNAMIC: { + VM_CASE(MACH_STORE_DYNAMIC): { JSValue obj = frame->slots[a]; JSValue key = frame->slots[b]; JSValue val = frame->slots[c]; @@ -1545,21 +1854,21 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (ret < 0) goto disrupt; mach_resolve_forward(&frame->slots[a]); - break; + VM_BREAK(); } /* New record */ - case MACH_NEWRECORD: { + VM_CASE(MACH_NEWRECORD): { JSValue obj = b > 0 ? JS_NewObjectCap(ctx, b) : JS_NewObject(ctx); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); if (JS_IsException(obj)) goto disrupt; frame->slots[a] = obj; - break; + VM_BREAK(); } /* Decomposed function calls (inlined from qbe_helpers) */ - case MACH_FRAME: - case MACH_GOFRAME: { + VM_CASE(MACH_FRAME): + VM_CASE(MACH_GOFRAME): { /* A=frame_slot, B=func_reg, C=argc */ JSValue func_val = frame->slots[b]; if (!mist_is_function(func_val)) { @@ -1577,15 +1886,15 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, func_val = frame->slots[b]; /* re-read after GC */ call_frame->function = func_val; frame->slots[a] = JS_MKPTR(call_frame); - break; + VM_BREAK(); } - case MACH_SETARG: { + VM_CASE(MACH_SETARG): { /* A=frame_slot, B=arg_idx, C=val_reg */ JSFrameRegister *fr = (JSFrameRegister *)JS_VALUE_GET_PTR(frame->slots[a]); fr->slots[b] = frame->slots[c]; - break; + VM_BREAK(); } - case MACH_INVOKE: { + VM_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->header); @@ -1650,9 +1959,9 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, #endif frame->slots[b] = ret; } - break; + VM_BREAK(); } - case MACH_GOINVOKE: { + VM_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->header); @@ -1715,24 +2024,24 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, int ret_slot = ret_info & 0xFFFF; if (ret_slot != 0xFFFF) frame->slots[ret_slot] = ret; } - break; + VM_BREAK(); } /* Jump if not null */ - case MACH_JMPNOTNULL: { + VM_CASE(MACH_JMPNOTNULL): { if (!JS_IsNull(frame->slots[a])) { int offset = MACH_GET_sBx(instr); pc = (uint32_t)((int32_t)pc + offset); } - break; + VM_BREAK(); } /* Disrupt (mcode alias) */ - case MACH_DISRUPT: + VM_CASE(MACH_DISRUPT): goto disrupt; /* Variable storage: env/global[K(Bx)] = R(A) */ - case MACH_SET_VAR: { + VM_CASE(MACH_SET_VAR): { int bx = MACH_GET_Bx(instr); JSValue key = code->cpool[bx]; JSValue val = frame->slots[a]; @@ -1743,26 +2052,29 @@ JSValue JS_CallRegisterVM(JSContext *ctx, JSCodeRegister *code, JS_SetProperty(ctx, ctx->global_obj, key, val); } frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); - break; + VM_BREAK(); } /* Has-property check (mcode name) */ - case MACH_IN: { + VM_CASE(MACH_IN): { JSValue key = frame->slots[b]; JSValue obj = frame->slots[c]; int has = JS_HasProperty(ctx, obj, key); frame = (JSFrameRegister *)JS_VALUE_GET_PTR(frame_ref.val); frame->slots[a] = JS_NewBool(ctx, has > 0); - break; + VM_BREAK(); } - default: + VM_DEFAULT: result = JS_ThrowInternalError(ctx, "unknown register VM opcode %d: %s", op, mach_opcode_names[op]); goto done; } continue; disrupt: + /* Save debug info for stack traces */ + ctx->reg_current_frame = frame_ref.val; + ctx->current_register_pc = pc > 0 ? pc - 1 : 0; /* Search frame chain for a disruption handler. Use frame_pc to track each frame's execution point: - For the faulting frame, it's the current pc.