From 73c8330cbd1091f3f6a262ece2449d8af96945b0 Mon Sep 17 00:00:00 2001 From: dearblue Date: Thu, 23 Nov 2023 22:38:01 +0900 Subject: [PATCH] RBreak remembers the CI location It is now possible to specify return destination directly. This allows callinfo to distinguish between calls to the same proc object. At the same time, the `Kernel#catch` method is adjusted. By removing the previously required double lambda object, the REnv object is no longer created as well. --- include/mruby/error.h | 27 ++++++---- mrbgems/mruby-catch/src/catch.c | 94 ++++++++++++--------------------- src/gc.c | 1 - src/vm.c | 43 ++++++--------- 4 files changed, 69 insertions(+), 96 deletions(-) diff --git a/include/mruby/error.h b/include/mruby/error.h index 5f7ec43bd..ee6fe8ffc 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -37,19 +37,30 @@ MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value st MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...); #if defined(MRB_64BIT) || defined(MRB_USE_FLOAT32) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) +#undef MRB_USE_RBREAK_VALUE_UNION +#else +#define MRB_USE_RBREAK_VALUE_UNION 1 +#endif + +/* + * flags: + * 0..7: enum mrb_vtype (only when defined MRB_USE_RBREAK_VALUE_UNION) + * 8..10: RBREAK_TAGs in src/vm.c (otherwise, set to 0) + */ struct RBreak { MRB_OBJECT_HEADER; - const struct RProc *proc; + uintptr_t ci_break_index; // The top-level ci index to break. One before the return destination. +#ifndef MRB_USE_RBREAK_VALUE_UNION mrb_value val; +#else + union mrb_value_union value; +#endif }; + +#ifndef MRB_USE_RBREAK_VALUE_UNION #define mrb_break_value_get(brk) ((brk)->val) #define mrb_break_value_set(brk, v) ((brk)->val = v) #else -struct RBreak { - MRB_OBJECT_HEADER; - const struct RProc *proc; - union mrb_value_union value; -}; #define RBREAK_VALUE_TT_MASK ((1 << 8) - 1) static inline mrb_value mrb_break_value_get(struct RBreak *brk) @@ -66,9 +77,7 @@ mrb_break_value_set(struct RBreak *brk, mrb_value val) brk->flags &= ~RBREAK_VALUE_TT_MASK; brk->flags |= val.tt; } -#endif /* MRB_64BIT || MRB_USE_FLOAT32 || MRB_NAN_BOXING || MRB_WORD_BOXING */ -#define mrb_break_proc_get(brk) ((brk)->proc) -#define mrb_break_proc_set(brk, p) ((brk)->proc = p) +#endif /* MRB_USE_RBREAK_VALUE_UNION */ /** * Error check diff --git a/mrbgems/mruby-catch/src/catch.c b/mrbgems/mruby-catch/src/catch.c index 44852c6b5..d655ada90 100644 --- a/mrbgems/mruby-catch/src/catch.c +++ b/mrbgems/mruby-catch/src/catch.c @@ -6,81 +6,56 @@ #include #include -MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_3, 1, MRB_SYM(call)) -static const mrb_code catch_iseq_3[18] = { - OP_ENTER, 0x00, 0x00, 0x00, // 000 ENTER 0:0:0:0:0:0:0 (0x0) - OP_GETUPVAR, 0x02, 0x02, 0x01, // 004 GETUPVAR R2 2 1 - OP_GETUPVAR, 0x03, 0x01, 0x01, // 008 GETUPVAR R3 1 1 - OP_SEND, 0x02, 0x00, 0x01, // 012 SEND R2 :call n=1 - OP_RETURN, 0x02, // 016 RETURN R2 -}; -static const mrb_irep catch_irep_3 = { - 2,5,0, - MRB_IREP_STATIC,catch_iseq_3, - NULL,catch_syms_3,NULL, - NULL, - NULL, - 18,0,1,0,0 -}; -static const mrb_irep *catch_reps_2[1] = { - &catch_irep_3, -}; -static const mrb_code catch_iseq_2[13] = { - OP_ENTER, 0x00, 0x00, 0x00, // 000 ENTER 0:0:0:0:0:0:0 (0x0) - OP_LAMBDA, 0x02, 0x00, // 004 LAMBDA R2 I[0] - OP_SEND, 0x02, 0x00, 0x00, // 007 SEND R2 :call n=0 - OP_RETURN, 0x02, // 011 RETURN R2 -}; -static const mrb_irep catch_irep_2 = { - 2,4,0, - MRB_IREP_STATIC,catch_iseq_2, - NULL,catch_syms_3,catch_reps_2, - NULL, - NULL, - 13,0,1,1,0 -}; -static const mrb_irep *catch_reps_1[1] = { - &catch_irep_2, -}; -MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms_1, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call)) -static const mrb_code catch_iseq_1[29] = { +MRB_PRESYM_DEFINE_VAR_AND_INITER(catch_syms, 3, MRB_SYM(Object), MRB_SYM(new), MRB_SYM(call)) +/* + * def catch(r1 = Object.new, &r2) + * r2.call(r1) + * end + */ +static const mrb_code catch_iseq[] = { OP_ENTER, 0x00, 0x20, 0x01, // 000 ENTER 0:1:0:0:0:0:1 (0x2001) - OP_JMP, 0x00, 0x03, // 004 JMP 010 - OP_JMP, 0x00, 0x0a, // 007 JMP 020 - OP_GETCONST, 0x03, 0x00, // 010 GETCONST R3 Object - OP_SEND, 0x03, 0x01, 0x00, // 013 SEND R3 :new n=0 - OP_MOVE, 0x01, 0x03, // 017 MOVE R1 R3 - OP_LAMBDA, 0x03, 0x00, // 020 LAMBDA R3 I[0] - OP_SEND, 0x03, 0x02, 0x00, // 023 SEND R3 :call n=0 - OP_RETURN, 0x03, // 027 RETURN R3 + OP_JMP, 0x00, 0x06, // 004 JMP 013 + + // copy for block parameter "tag" when method argument are given + OP_MOVE, 0x03, 0x01, // 007 MOVE R3 R1 + OP_JMP, 0x00, 0x0a, // 010 JMP 023 + + // create a tag for default parameter + OP_GETCONST, 0x03, 0x00, // 013 GETCONST R3 Object + OP_SEND, 0x03, 0x01, 0x00, // 016 SEND R3 :new n=0 + OP_MOVE, 0x01, 0x03, // 020 MOVE R1 R3 + + // to save on the stack, block variables are used as is + OP_SEND, 0x02, 0x02, 0x01, // 023 SEND R2 :call n=1 + OP_RETURN, 0x02, // 027 RETURN R2 }; static const mrb_irep catch_irep = { 3,5,0, - MRB_IREP_STATIC,catch_iseq_1, - NULL,catch_syms_1,catch_reps_1, + MRB_IREP_STATIC,catch_iseq, + NULL,catch_syms,NULL, NULL, NULL, - 29,0,3,1,0 + sizeof(catch_iseq),0,3,0,0 }; static const struct RProc catch_proc = { NULL, NULL, MRB_TT_PROC, MRB_GC_RED, MRB_FL_OBJ_IS_FROZEN | MRB_PROC_SCOPE | MRB_PROC_STRICT, { &catch_irep }, NULL, { NULL } }; -static const mrb_callinfo * +static uintptr_t find_catcher(mrb_state *mrb, mrb_value tag) { - const mrb_callinfo *ci = mrb->c->ci; - size_t n = ci - mrb->c->cibase; - ci--; + const mrb_callinfo *ci = mrb->c->ci - 1; // skip ownself throw + ptrdiff_t n = ci - mrb->c->cibase; + for (; n > 0; n--, ci--) { const mrb_value *arg1 = ci->stack + 1; if (ci->proc == &catch_proc && mrb_obj_eq(mrb, *arg1, tag)) { - return ci; + return (uintptr_t)n; } } - return NULL; + return 0; } static mrb_value @@ -91,11 +66,11 @@ throw_m(mrb_state *mrb, mrb_value self) obj = mrb_nil_value(); } - const mrb_callinfo *ci = find_catcher(mrb, tag); - if (ci) { + uintptr_t ci_index = find_catcher(mrb, tag); + if (ci_index > 0) { struct RBreak *b = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); mrb_break_value_set(b, obj); - mrb_break_proc_set(b, ci[2].proc); /* Back to the closure in `catch` method */ + b->ci_break_index = ci_index; /* Back to the caller directly */ mrb_exc_raise(mrb, mrb_obj_value(b)); } else { @@ -111,8 +86,7 @@ mrb_mruby_catch_gem_init(mrb_state *mrb) { mrb_method_t m; - MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_3); - MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms_1); + MRB_PRESYM_INIT_SYMBOLS(mrb, catch_syms); MRB_METHOD_FROM_PROC(m, &catch_proc); mrb_define_method_raw(mrb, mrb->kernel_module, MRB_SYM(catch), m); diff --git a/src/gc.c b/src/gc.c index 7c4461e3b..9eb84311b 100644 --- a/src/gc.c +++ b/src/gc.c @@ -677,7 +677,6 @@ gc_mark_children(mrb_state *mrb, mrb_gc *gc, struct RBasic *obj) case MRB_TT_BREAK: { struct RBreak *brk = (struct RBreak*)obj; - mrb_gc_mark(mrb, (struct RBasic*)mrb_break_proc_get(brk)); mrb_gc_mark_value(mrb, mrb_break_value_get(brk)); } break; diff --git a/src/vm.c b/src/vm.c index 6d8c74612..f03acfe81 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1104,12 +1104,12 @@ mrb_break_tag_set(struct RBreak *brk, uint32_t tag) } static struct RBreak* -break_new(mrb_state *mrb, uint32_t tag, const struct RProc *p, mrb_value val) +break_new(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, mrb_value val) { - struct RBreak *brk; + mrb_assert((size_t)(return_ci - mrb->c->cibase) <= (size_t)(mrb->c->ci - mrb->c->cibase)); - brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); - mrb_break_proc_set(brk, p); + struct RBreak *brk = MRB_OBJ_ALLOC(mrb, MRB_TT_BREAK, NULL); + brk->ci_break_index = return_ci - mrb->c->cibase; mrb_break_value_set(brk, val); mrb_break_tag_set(brk, tag); @@ -1213,27 +1213,27 @@ break_tag_p(struct RBreak *brk, uint32_t tag) } static void -prepare_tagged_break(mrb_state *mrb, uint32_t tag, const struct RProc *proc, mrb_value val) +prepare_tagged_break(mrb_state *mrb, uint32_t tag, const mrb_callinfo *return_ci, 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); + mrb->exc = (struct RObject*)break_new(mrb, tag, return_ci, val); } } -#define THROW_TAGGED_BREAK(mrb, tag, proc, val) \ +#define THROW_TAGGED_BREAK(mrb, tag, return_ci, val) \ do { \ - prepare_tagged_break(mrb, tag, proc, val); \ + prepare_tagged_break(mrb, tag, return_ci, val); \ goto L_CATCH_TAGGED_BREAK; \ } while (0) -#define UNWIND_ENSURE(mrb, ci, pc, tag, proc, val) \ +#define UNWIND_ENSURE(mrb, ci, pc, tag, return_ci, val) \ do { \ ch = catch_handler_find(mrb, ci, pc, MRB_CATCH_FILTER_ENSURE); \ if (ch) { \ - THROW_TAGGED_BREAK(mrb, tag, proc, val); \ + THROW_TAGGED_BREAK(mrb, tag, return_ci, val); \ } \ } while (0) @@ -1696,7 +1696,7 @@ RETRY_TRY_BLOCK: if (ch) { /* avoiding a jump from a catch handler into the same handler */ if (a < mrb_irep_catch_handler_unpack(ch->begin) || a >= mrb_irep_catch_handler_unpack(ch->end)) { - THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, proc, mrb_fixnum_value(a)); + THROW_TAGGED_BREAK(mrb, RBREAK_TAG_JUMP, mrb->c->ci, mrb_fixnum_value(a)); } } } @@ -2295,7 +2295,6 @@ RETRY_TRY_BLOCK: localjump_error(mrb, LOCALJUMP_ERROR_RETURN); goto L_RAISE; } - proc = dst; break; } /* fallthrough */ @@ -2348,18 +2347,14 @@ RETRY_TRY_BLOCK: if (ci == mrb->c->cibase) { goto L_BREAK_ERROR; } - proc = ci->proc; if (FALSE) { struct RBreak *brk; L_BREAK: brk = (struct RBreak*)mrb->exc; - proc = mrb_break_proc_get(brk); + ci = &mrb->c->cibase[brk->ci_break_index]; + proc = ci->proc; v = mrb_break_value_get(brk); - ci = mrb->c->ci; - while (mrb->c->cibase < ci && ci->proc != proc) { - ci--; - } pc = ci->pc; switch (mrb_break_tag_get(brk)) { @@ -2379,16 +2374,12 @@ RETRY_TRY_BLOCK: for (;;) { CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) { struct RBreak *brk = (struct RBreak*)mrb->exc; - proc = mrb_break_proc_get(brk); - ci = mrb->c->ci; - while (mrb->c->cibase <= ci && ci->proc != proc) { - ci--; - } + ci = &mrb->c->cibase[brk->ci_break_index]; v = mrb_break_value_get(brk); mrb_gc_protect(mrb, v); } CHECKPOINT_MAIN(RBREAK_TAG_BREAK) { - UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_BREAK, proc, v); + UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_BREAK, ci, v); } CHECKPOINT_END(RBREAK_TAG_BREAK); @@ -2397,7 +2388,7 @@ RETRY_TRY_BLOCK: } cipop(mrb); if (mrb->c->ci[1].cci != CINFO_NONE) { - mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, proc, v); + mrb->exc = (struct RObject*)break_new(mrb, RBREAK_TAG_BREAK, ci, v); mrb_gc_arena_restore(mrb, ai); mrb->c->vmexec = FALSE; mrb->jmp = prev_jmp; @@ -3092,7 +3083,7 @@ RETRY_TRY_BLOCK: /* do nothing */ } CHECKPOINT_MAIN(RBREAK_TAG_STOP) { - UNWIND_ENSURE(mrb, mrb->c->ci, pc, RBREAK_TAG_STOP, proc, mrb_nil_value()); + UNWIND_ENSURE(mrb, mrb->c->ci, mrb->c->ci->pc, RBREAK_TAG_STOP, mrb->c->ci, mrb_nil_value()); } CHECKPOINT_END(RBREAK_TAG_STOP); L_STOP: