Merge pull request #6103 from dearblue/RBreak-ci

RBreak remembers the CI location
This commit is contained in:
Yukihiro "Matz" Matsumoto
2023-12-09 11:35:23 +09:00
committed by GitHub
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: