diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/codedump.c | 3 | ||||
| -rw-r--r-- | src/gc.c | 8 | ||||
| -rw-r--r-- | src/state.c | 2 | ||||
| -rw-r--r-- | src/vm.c | 339 |
4 files changed, 241 insertions, 111 deletions
diff --git a/src/codedump.c b/src/codedump.c index 81212921d..095028a39 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -261,6 +261,9 @@ codedump(mrb_state *mrb, const mrb_irep *irep) CASE(OP_JMP, S); printf("OP_JMP\t\t%03d\n", a); break; + CASE(OP_JUW, S); + printf("OP_JUW\t\t%03d\n", a); + break; CASE(OP_JMPIF, BS); printf("OP_JMPIF\tR%d\t%03d\t", a, b); print_lv_a(mrb, irep, a); @@ -640,7 +640,6 @@ mark_context_stack(mrb_state *mrb, struct mrb_context *c) static void mark_context(mrb_state *mrb, struct mrb_context *c) { - int i; mrb_callinfo *ci; start: @@ -657,10 +656,6 @@ mark_context(mrb_state *mrb, struct mrb_context *c) mrb_gc_mark(mrb, (struct RBasic*)ci->target_class); } } - /* mark ensure stack */ - for (i=0; i<c->eidx; i++) { - mrb_gc_mark(mrb, (struct RBasic*)c->ensure[i]); - } /* mark fibers */ mrb_gc_mark(mrb, (struct RBasic*)c->fib); if (c->prev) { @@ -1014,9 +1009,6 @@ gc_gray_counts(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) if (c->stbase + i > c->stend) i = c->stend - c->stbase; children += i; - /* mark ensure stack */ - children += c->eidx; - /* mark closure */ if (c->cibase) { for (i=0, ci = c->cibase; ci <= c->ci; i++, ci++) diff --git a/src/state.c b/src/state.c index 7a1283fa6..1f85448a1 100644 --- a/src/state.c +++ b/src/state.c @@ -171,8 +171,6 @@ mrb_free_context(mrb_state *mrb, struct mrb_context *c) if (!c) return; mrb_free(mrb, c->stbase); mrb_free(mrb, c->cibase); - mrb_free(mrb, c->rescue); - mrb_free(mrb, c->ensure); mrb_free(mrb, c); } @@ -23,6 +23,7 @@ #include <mruby/opcode.h> #include "value_array.h" #include <mruby/throw.h> +#include <mruby/dump.h> #ifdef MRB_DISABLE_STDIO #if defined(__cplusplus) @@ -280,8 +281,6 @@ cipush(mrb_state *mrb, const mrb_code *pc, int push_stacks, int acc, ci->mid = mid; ci->proc = proc; ci->stackent = c->stack; - ci->epos = c->eidx; - ci->ridx = ci[-1].ridx; ci->pc = pc; ci->argc = argc; ci->acc = acc; @@ -329,54 +328,6 @@ cipop(mrb_state *mrb) void mrb_exc_set(mrb_state *mrb, mrb_value exc); static mrb_value mrb_run(mrb_state *mrb, const struct RProc* proc, mrb_value self); -static void -ecall(mrb_state *mrb) -{ - struct RProc *p; - struct mrb_context *c = mrb->c; - mrb_callinfo *ci = c->ci; - struct RObject *exc; - struct REnv *env; - ptrdiff_t cioff; - int ai = mrb_gc_arena_save(mrb); - uint16_t i; - int nregs; - - if (c->eidx == 0) return; - i = --c->eidx; - - /* restrict total call depth of ecall() */ - if (++mrb->ecall_nest > MRB_ECALL_DEPTH_MAX) { - mrb_exc_raise(mrb, mrb_obj_value(mrb->stack_err)); - } - p = c->ensure[i]; - if (!p) return; - mrb_assert(!MRB_PROC_CFUNC_P(p)); - c->ensure[i] = NULL; - nregs = p->upper->body.irep->nregs; - if (ci->proc && !MRB_PROC_CFUNC_P(ci->proc) && - ci->proc->body.irep->nregs > nregs) { - nregs = ci->proc->body.irep->nregs; - } - cioff = ci - c->cibase; - ci = cipush(mrb, NULL, nregs, CI_ACC_SKIP, MRB_PROC_TARGET_CLASS(p), p, ci->mid, 0); - env = MRB_PROC_ENV(p); - mrb_assert(env); - exc = mrb->exc; mrb->exc = 0; - if (exc) { - mrb_gc_protect(mrb, mrb_obj_value(exc)); - } - if (mrb->c->fib) { - mrb_gc_protect(mrb, mrb_obj_value(mrb->c->fib)); - } - mrb_run(mrb, p, env->stack[0]); - mrb->c = c; - c->ci = c->cibase + cioff; - if (!mrb->exc) mrb->exc = exc; - mrb_gc_arena_restore(mrb, ai); - mrb->ecall_nest--; -} - #ifndef MRB_FUNCALL_ARGC_MAX #define MRB_FUNCALL_ARGC_MAX 16 #endif @@ -821,17 +772,56 @@ mrb_yield_cont(mrb_state *mrb, mrb_value b, mrb_value self, mrb_int argc, const } static struct RBreak* -break_new(mrb_state *mrb, const struct RProc *p, mrb_value val) +break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val) { struct RBreak *brk; brk = (struct RBreak*)mrb_obj_alloc(mrb, MRB_TT_BREAK, NULL); mrb_break_proc_set(brk, p); mrb_break_value_set(brk, val); + mrb_break_tag_set(brk, tag); return brk; } +#define MRB_CATCH_FILTER_RESCUE (UINT32_C(1) << MRB_CATCH_RESCUE) +#define MRB_CATCH_FILTER_ENSURE (UINT32_C(1) << MRB_CATCH_ENSURE) +#define MRB_CATCH_FILTER_ALL (MRB_CATCH_FILTER_RESCUE | MRB_CATCH_FILTER_ENSURE) + +static const struct mrb_irep_catch_hander * +catch_handler_find(mrb_state *mrb, mrb_callinfo *ci, const mrb_code *pc, uint32_t filter) +{ + mrb_irep *irep; + ptrdiff_t xpc; + size_t cnt; + const struct mrb_irep_catch_hander *e; + +/* The comparison operators use `>` and `<=` because pc already points to the next instruction */ +#define catch_cover_p(pc, beg, end) ((pc) > (beg) && (pc) <= (end)) + + if (ci->proc == NULL || MRB_PROC_CFUNC_P(ci->proc)) return NULL; + irep = ci->proc->body.irep; + if (irep->clen < 1) return NULL; + xpc = pc - irep->iseq; + /* If it retry at the top level, pc will be 0, so check with -1 as the start position */ + mrb_assert(catch_cover_p(xpc, -1, irep->ilen)); + if (!catch_cover_p(xpc, -1, irep->ilen)) return NULL; + + /* Currently uses a simple linear search to avoid processing complexity. */ + cnt = irep->clen; + e = mrb_irep_catch_handler_table(irep) + cnt - 1; + for (; cnt > 0; cnt --, e --) { + if (((UINT32_C(1) << e->type) & filter) && + catch_cover_p(xpc, bin_to_uint16(e->begin), bin_to_uint16(e->end))) { + return e; + } + } + +#undef catch_cover_p + + return NULL; +} + typedef enum { LOCALJUMP_ERROR_RETURN = 0, LOCALJUMP_ERROR_BREAK = 1, @@ -878,6 +868,81 @@ argnum_error(mrb_state *mrb, mrb_int num) mrb_exc_set(mrb, exc); } +static mrb_bool +break_tag_p(struct RBreak *brk, uint32_t tag) +{ + return (brk != NULL && brk->tt == MRB_TT_BREAK) ? TRUE : FALSE; +} + +static void +prepare_tagged_break(mrb_state *mrb, uint32_t tag, struct RProc *proc, mrb_value val) +{ + if (break_tag_p((struct RBreak*)mrb->exc, tag)) { + mrb_break_tag_set((struct RBreak*)mrb->exc, tag); + } + else { + mrb->exc = (struct RObject*)break_new(mrb, tag, proc, val); + } +} + +#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \ + do { \ + prepare_tagged_break(mrb, tag, proc, val); \ + goto L_CATCH_TAGGED_BREAK; \ + } while (0) + +#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \ + do { \ + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \ + if (ch) { \ + THROW_TAGGED_BREAK(mrb, tag, proc, val); \ + } \ + } while (0) + +/* + * CHECKPOINT_RESTORE(tag) { + * This part is executed when jumping by the same "tag" of RBreak (it is not executed the first time). + * Write the code required (initialization of variables, etc.) for the subsequent processing. + * } + * CHECKPOINT_MAIN(tag) { + * This part is always executed. + * } + * CHECKPOINT_END(tag); + * + * ... + * + * // Jump to CHECKPOINT_RESTORE with the same "tag". + * goto CHECKPOINT_LABEL_MAKE(tag); + */ + +#define CHECKPOINT_LABEL_MAKE(tag) L_CHECKPOINT_ ## tag + +#define CHECKPOINT_RESTORE(tag) \ + do { \ + DEBUG_ONLY_EXPR(int current_checkpoint_tag); \ + DEBUG_ONLY_EXPR(current_checkpoint_tag = (tag)); \ + if (FALSE) { \ + CHECKPOINT_LABEL_MAKE(tag): \ + DEBUG_ONLY_EXPR(current_checkpoint_tag = (tag)); \ + do { + +#define CHECKPOINT_MAIN(tag) \ + } while (0); \ + } \ + mrb_assert((tag) == current_checkpoint_tag); \ + do { + +#define CHECKPOINT_END(tag) \ + } while (0); \ + mrb_assert((tag) == current_checkpoint_tag); \ + } while (0) + +#ifdef MRB_DEBUG +#define DEBUG_ONLY_EXPR(e) e +#else +#define DEBUG_ONLY_EXPR(e) ((void)0) +#endif + #define ERR_PC_SET(mrb) mrb->c->ci->err = pc0; #define ERR_PC_CLR(mrb) mrb->c->ci->err = 0; #ifdef MRB_ENABLE_DEBUG_HOOK @@ -976,6 +1041,7 @@ mrb_vm_exec(mrb_state *mrb, const struct RProc *proc, const mrb_code *pc) uint16_t b; uint8_t c; mrb_sym mid; + const struct mrb_irep_catch_hander *ch; #ifdef DIRECT_THREADED static void *optable[] = { @@ -1231,6 +1297,30 @@ RETRY_TRY_BLOCK: NEXT; } + CASE(OP_JUW, S) { + CHECKPOINT_RESTORE(RBREAK_TAG_JUMP) { + struct RBreak *brk = (struct RBreak*)mrb->exc; + mrb_value target = mrb_break_value_get(brk); + mrb_assert(mrb_fixnum_p(target)); + a = mrb_fixnum(target); + mrb_assert(a >= 0 && a < irep->ilen); + } + CHECKPOINT_MAIN(RBREAK_TAG_JUMP) { + ch = catch_handler_find(mrb, mrb->c->ci, pc, MRB_CATCH_FILTER_ENSURE); + if (ch) { + /* avoiding a jump from a catch handler into the same handler */ + if (a < bin_to_uint16(ch->begin) || a >= bin_to_uint16(ch->end)) { + THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a)); + } + } + } + CHECKPOINT_END(RBREAK_TAG_JUMP); + + mrb->exc = NULL; /* clear break object */ + pc = irep->iseq + a; + JUMP; + } + CASE(OP_EXCEPT, B) { mrb_value exc; @@ -1563,6 +1653,7 @@ RETRY_TRY_BLOCK: mrb_gc_arena_restore(mrb, ai); if (mrb->exc) goto L_RAISE; ci = mrb->c->ci; + mrb_assert(!mrb_break_p(v)); if (!ci->target_class) { /* return from context modifying method (resume/yield) */ if (ci->acc == CI_ACC_RESUMED) { mrb->jmp = prev_jmp; @@ -1839,13 +1930,7 @@ RETRY_TRY_BLOCK: c = OP_R_NORMAL; L_RETURN: { - mrb_callinfo *ci; - -#define ecall_adjust() do {\ - ptrdiff_t cioff = ci - mrb->c->cibase;\ - ecall(mrb);\ - ci = mrb->c->cibase + cioff;\ -} while (0) + mrb_callinfo *ci; ci = mrb->c->ci; if (ci->mid) { @@ -1873,17 +1958,20 @@ RETRY_TRY_BLOCK: L_RAISE: ci0 = ci = mrb->c->ci; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) goto L_FTOP; - goto L_RESCUE; + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL); + if (ch == NULL) goto L_FTOP; + goto L_CATCH; } - while (ci[0].ridx == ci[-1].ridx) { + while ((ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL)) == NULL) { ci = cipop(mrb); if (ci[1].acc == CI_ACC_SKIP && prev_jmp) { mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } + pc = ci[1].pc; if (ci == mrb->c->cibase) { - if (ci->ridx == 0) { + ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ALL); + if (ch == NULL) { L_FTOP: /* fiber top */ if (mrb->c == mrb->root_c) { mrb->c->stack = mrb->c->stbase; @@ -1892,9 +1980,6 @@ RETRY_TRY_BLOCK: else { struct mrb_context *c = mrb->c; - while (c->eidx > ci->epos) { - ecall_adjust(); - } c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; c->prev = NULL; @@ -1903,15 +1988,13 @@ RETRY_TRY_BLOCK: } break; } - /* call ensure only when we skip this callinfo */ - if (ci[0].ridx == ci[-1].ridx) { - while (mrb->c->eidx > ci->epos) { - ecall_adjust(); - } - } } - L_RESCUE: - if (ci->ridx == 0) goto L_STOP; + L_CATCH: + if (ch == NULL) goto L_STOP; + if (FALSE) { + L_CATCH_TAGGED_BREAK: /* from THROW_TAGGED_BREAK() or UNWIND_ENSURE() */ + ci = ci0 = mrb->c->ci; + } proc = ci->proc; irep = proc->body.irep; pool = irep->pool; @@ -1920,7 +2003,7 @@ RETRY_TRY_BLOCK: mrb->c->stack = ci[1].stackent; } mrb_stack_extend(mrb, irep->nregs); - pc = irep->iseq+mrb->c->rescue[--ci->ridx]; + pc = irep->iseq + bin_to_uint16(ch->target); } else { int acc; @@ -1950,8 +2033,19 @@ RETRY_TRY_BLOCK: localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } - ci--; + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_BLOCK) { + cibase = mrb->c->cibase; + dst = top_proc(mrb, proc); + } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN_BLOCK) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_BLOCK, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN_BLOCK); + pc = ci->pc; + ci = cipop(mrb); } + mrb->exc = NULL; /* clear break object */ + proc = ci->proc; if (ci <= cibase) { localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; @@ -1966,16 +2060,20 @@ RETRY_TRY_BLOCK: if (!c->prev) { /* toplevel return */ regs[irep->nlocals] = v; - goto L_STOP; + goto CHECKPOINT_LABEL_MAKE(RBREAK_TAG_STOP); } if (c->prev->ci == c->prev->cibase) { mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); mrb_exc_set(mrb, exc); goto L_RAISE; } - while (c->eidx > 0) { - ecall(mrb); + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN_TOPLEVEL) { + c = mrb->c; } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN_TOPLEVEL) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN_TOPLEVEL, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN_TOPLEVEL); /* automatic yield at the end */ c->status = MRB_FIBER_TERMINATED; mrb->c = c->prev; @@ -1983,6 +2081,14 @@ RETRY_TRY_BLOCK: mrb->c->status = MRB_FIBER_RUNNING; ci = mrb->c->ci; } + CHECKPOINT_RESTORE(RBREAK_TAG_RETURN) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_RETURN) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_RETURN, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_RETURN); + mrb->exc = NULL; /* clear break object */ break; case OP_R_BREAK: if (MRB_PROC_STRICT_P(proc)) goto NORMAL_RETURN; @@ -2005,9 +2111,13 @@ RETRY_TRY_BLOCK: goto L_BREAK_ERROR; } } - while (mrb->c->eidx > mrb->c->ci->epos) { - ecall_adjust(); + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { + /* do nothing */ } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_BREAK); /* break from fiber block */ if (ci == mrb->c->cibase && ci->pc) { struct mrb_context *c = mrb->c; @@ -2017,45 +2127,64 @@ RETRY_TRY_BLOCK: ci = mrb->c->ci; } if (ci->acc < 0) { + ci = cipop(mrb); mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; - mrb->exc = (struct RObject*)break_new(mrb, proc, v); + mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v); mrb->jmp = prev_jmp; MRB_THROW(prev_jmp); } if (FALSE) { + struct RBreak *brk; + L_BREAK: - v = mrb_break_value_get((struct RBreak*)mrb->exc); - proc = mrb_break_proc_get((struct RBreak*)mrb->exc); - mrb->exc = NULL; + brk = (struct RBreak*)mrb->exc; + proc = mrb_break_proc_get(brk); + v = mrb_break_value_get(brk); ci = mrb->c->ci; + + switch (mrb_break_tag_get(brk)) { +#define DISPATCH_CHECKPOINTS(n, i) case n: goto CHECKPOINT_LABEL_MAKE(n); + RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS) +#undef DISPATCH_CHECKPOINTS + default: + mrb_assert(!"wrong break tag"); + } } mrb->c->stack = ci->stackent; - proc = proc->upper; - while (mrb->c->cibase < ci && ci[-1].proc != proc) { + while (mrb->c->cibase < ci && ci[-1].proc != proc->upper) { if (ci[-1].acc == CI_ACC_SKIP) { - while (ci < mrb->c->ci) { - cipop(mrb); - } goto L_BREAK_ERROR; } - ci--; + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_UPPER) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK_UPPER) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_UPPER, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_BREAK_UPPER); + pc = ci->pc; + ci = cipop(mrb); } + CHECKPOINT_RESTORE(RBREAK_TAG_BREAK_INTARGET) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_BREAK_INTARGET) { + UNWIND_ENSURE(mrb, ci, pc, RBREAK_TAG_BREAK_INTARGET, proc, v); + } + CHECKPOINT_END(RBREAK_TAG_BREAK_INTARGET); if (ci == mrb->c->cibase) { goto L_BREAK_ERROR; } + mrb->exc = NULL; /* clear break object */ break; default: /* cannot happen */ break; } - while (ci < mrb->c->ci) { - cipop(mrb); - } - ci[0].ridx = ci[-1].ridx; - while (mrb->c->eidx > ci->epos) { - ecall_adjust(); - } + mrb_assert(ci == mrb->c->ci); + mrb_assert(mrb->exc == NULL); + if (mrb->c->vmexec && !ci->target_class) { mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; @@ -2679,14 +2808,18 @@ RETRY_TRY_BLOCK: CASE(OP_STOP, Z) { /* stop VM */ - L_STOP: - while (mrb->c->eidx > 0) { - ecall(mrb); + CHECKPOINT_RESTORE(RBREAK_TAG_STOP) { + /* do nothing */ + } + CHECKPOINT_MAIN(RBREAK_TAG_STOP) { + UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value()); } - mrb->c->cibase->ridx = 0; + CHECKPOINT_END(RBREAK_TAG_STOP); + L_STOP: ERR_PC_CLR(mrb); mrb->jmp = prev_jmp; if (mrb->exc) { + mrb_assert(mrb->exc->tt == MRB_TT_EXCEPTION); return mrb_obj_value(mrb->exc); } return regs[irep->nlocals]; @@ -2696,6 +2829,10 @@ RETRY_TRY_BLOCK: #undef regs } MRB_CATCH(&c_jmp) { + mrb_callinfo *ci = mrb->c->ci; + while (ci > mrb->c->cibase && ci->acc == CI_ACC_DIRECT) { + ci = cipop(mrb); + } exc_catched = TRUE; goto RETRY_TRY_BLOCK; } |
