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.
This commit is contained in:
dearblue
2023-11-23 22:38:01 +09:00
parent 658d5a51ac
commit 73c8330cbd
4 changed files with 69 additions and 96 deletions
+18 -9
View File
@@ -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
+34 -60
View File
@@ -6,81 +6,56 @@
#include <mruby/opcode.h>
#include <mruby/presym.h>
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);
-1
View File
@@ -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;
+17 -26
View File
@@ -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: