From 859f1b300251eb10403baf8874bc247c251ef66b Mon Sep 17 00:00:00 2001 From: dearblue Date: Wed, 20 Dec 2023 21:54:53 +0900 Subject: [PATCH] Separate unwinding process for `break` / `return` --- include/mruby/opcode.h | 4 - src/vm.c | 172 +++++++++++++++++++++-------------------- 2 files changed, 89 insertions(+), 87 deletions(-) diff --git a/include/mruby/opcode.h b/include/mruby/opcode.h index 249598161..19da77fba 100644 --- a/include/mruby/opcode.h +++ b/include/mruby/opcode.h @@ -19,10 +19,6 @@ enum mrb_insn { #define OP_L_LAMBDA (OP_L_STRICT|OP_L_CAPTURE) #define OP_L_BLOCK OP_L_CAPTURE -#define OP_R_NORMAL 0 -#define OP_R_BREAK 1 -#define OP_R_RETURN 2 - #define PEEK_B(pc) (*(pc)) #define PEEK_S(pc) ((pc)[0]<<8|(pc)[1]) #define PEEK_W(pc) ((pc)[0]<<16|(pc)[1]<<8|(pc)[2]) diff --git a/src/vm.c b/src/vm.c index 38ecffc95..390d0742b 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1971,7 +1971,6 @@ RETRY_TRY_BLOCK: if (!irep) { mrb->c->ci->stack[0] = mrb_nil_value(); a = 0; - c = OP_R_NORMAL; goto L_OP_RETURN_BODY; } mrb_int nargs = ci_bidx(ci)+1; @@ -2266,17 +2265,75 @@ RETRY_TRY_BLOCK: } CASE(OP_BREAK, B) { - c = OP_R_BREAK; - goto L_RETURN; + if (mrb->exc) { + goto L_RAISE; + } + + if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; + if (MRB_PROC_ORPHAN_P(proc) || !MRB_PROC_ENV_P(proc) || !MRB_ENV_ONSTACK_P(MRB_PROC_ENV(proc))) { + L_BREAK_ERROR: + RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "break from proc-closure"); + } + else { + struct REnv *e = MRB_PROC_ENV(proc); + + if (e->cxt != mrb->c) { + goto L_BREAK_ERROR; + } + } + mrb_callinfo *ci = mrb->c->ci; + proc = proc->upper; + while (mrb->c->cibase < ci && ci[-1].proc != proc) { + ci--; + } + if (ci == mrb->c->cibase) { + goto L_BREAK_ERROR; + } + c = a; // release the "a" variable, which can handle 32-bit values + a = ci - mrb->c->cibase; + goto L_UNWINDING; } CASE(OP_RETURN_BLK, B) { - c = OP_R_RETURN; - goto L_RETURN; + if (mrb->exc) { + goto L_RAISE; + } + + mrb_callinfo *ci = mrb->c->ci; + + if (ci->cci != CINFO_NONE || !MRB_PROC_ENV_P(proc) || MRB_PROC_STRICT_P(proc)) { + goto NORMAL_RETURN; + } + + const struct RProc *dst; + mrb_callinfo *cibase; + cibase = mrb->c->cibase; + dst = top_proc(mrb, proc); + + if (MRB_PROC_ENV_P(dst)) { + struct REnv *e = MRB_PROC_ENV(dst); + + if (!MRB_ENV_ONSTACK_P(e) || (e->cxt && e->cxt != mrb->c)) { + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + } + /* check jump destination */ + while (cibase <= ci && ci->proc != dst) { + if (ci->cci > CINFO_NONE) { /* jump cross C boundary */ + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + ci--; + } + if (ci <= cibase) { /* no jump destination */ + localjump_error(mrb, LOCALJUMP_ERROR_RETURN); + goto L_RAISE; + } + c = a; // release the "a" variable, which can handle 32-bit values + a = ci - mrb->c->cibase; + goto L_UNWINDING; } - CASE(OP_RETURN, B) - c = OP_R_NORMAL; - L_RETURN: - { + CASE(OP_RETURN, B) { mrb_callinfo *ci; ci = mrb->c->ci; @@ -2287,92 +2344,41 @@ RETRY_TRY_BLOCK: mrb_int acc; mrb_value v; + NORMAL_RETURN: ci = mrb->c->ci; v = regs[a]; mrb_gc_protect(mrb, v); - switch (c) { - case OP_R_RETURN: - /* Fall through to OP_R_NORMAL otherwise */ - if (ci->cci == CINFO_NONE && MRB_PROC_ENV_P(proc) && !MRB_PROC_STRICT_P(proc)) { - const struct RProc *dst; - mrb_callinfo *cibase; - cibase = mrb->c->cibase; - dst = top_proc(mrb, proc); - if (MRB_PROC_ENV_P(dst)) { - struct REnv *e = MRB_PROC_ENV(dst); + if (ci == mrb->c->cibase) { + struct mrb_context *c; + c = mrb->c; - if (!MRB_ENV_ONSTACK_P(e) || (e->cxt && e->cxt != mrb->c)) { - localjump_error(mrb, LOCALJUMP_ERROR_RETURN); - goto L_RAISE; - } + if (!c->prev) { + if (c != mrb->root_c) { + /* fiber termination should transfer to root */ + c->prev = mrb->root_c; } - /* check jump destination */ - while (cibase <= ci && ci->proc != dst) { - if (ci->cci > CINFO_NONE) { /* jump cross C boundary */ - localjump_error(mrb, LOCALJUMP_ERROR_RETURN); - goto L_RAISE; - } - ci--; - } - if (ci <= cibase) { /* no jump destination */ - localjump_error(mrb, LOCALJUMP_ERROR_RETURN); - goto L_RAISE; - } - break; - } - /* fallthrough */ - case OP_R_NORMAL: - NORMAL_RETURN: - if (ci == mrb->c->cibase) { - struct mrb_context *c; - c = mrb->c; - - if (!c->prev) { - if (c != mrb->root_c) { - /* fiber termination should transfer to root */ - c->prev = mrb->root_c; - } - else { /* toplevel return */ - regs[irep->nlocals] = v; - goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP); - } - } - else if (!c->vmexec && c->prev->ci == c->prev->cibase) { - RAISE_LIT(mrb, E_FIBER_ERROR, "double resume"); + else { /* toplevel return */ + regs[irep->nlocals] = v; + goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP); } } - break; - case OP_R_BREAK: - if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; - if (MRB_PROC_ORPHAN_P(proc) || !MRB_PROC_ENV_P(proc) || !MRB_ENV_ONSTACK_P(MRB_PROC_ENV(proc))) { - L_BREAK_ERROR: - RAISE_LIT(mrb, E_LOCALJUMP_ERROR, "break from proc-closure"); + else if (!c->vmexec && c->prev->ci == c->prev->cibase) { + RAISE_LIT(mrb, E_FIBER_ERROR, "double resume"); } - else { - struct REnv *e = MRB_PROC_ENV(proc); - - if (e->cxt != mrb->c) { - goto L_BREAK_ERROR; - } - } - proc = proc->upper; - while (mrb->c->cibase < ci && ci[-1].proc != proc) { - ci--; - } - if (ci == mrb->c->cibase) { - goto L_BREAK_ERROR; - } - break; - default: - /* cannot happen */ - break; } CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { - struct RBreak *brk = (struct RBreak*)mrb->exc; - ci = &mrb->c->cibase[brk->ci_break_index]; - v = mrb_break_value_get(brk); + if (TRUE) { + struct RBreak *brk = (struct RBreak*)mrb->exc; + ci = &mrb->c->cibase[brk->ci_break_index]; + v = mrb_break_value_get(brk); + } + else { + L_UNWINDING: // for a check on the role of `a` and `c`, see `goto L_UNWINDING` + ci = mrb->c->cibase + a; + v = regs[c]; + } mrb_gc_protect(mrb, v); } CHECKPOINT_MAIN(RBREAK_TAG_BREAK) {