diff options
| author | dearblue <[email protected]> | 2020-07-18 19:05:12 +0900 |
|---|---|---|
| committer | Yukihiro "Matz" Matsumoto <[email protected]> | 2020-10-12 16:21:33 +0900 |
| commit | c1f112c49a597da8478516261f0812bc073f6cd8 (patch) | |
| tree | c5f7f8d805f7bf767a06a10e9b7ef82071cfcee2 /include | |
| parent | 0d7b4deccf445e3edae9239bf40b91cc79e83634 (diff) | |
| download | mruby-c1f112c49a597da8478516261f0812bc073f6cd8.tar.gz mruby-c1f112c49a597da8478516261f0812bc073f6cd8.zip | |
Replace global jump with catch handler implementation
When a global jump occurs, look at the catch handler table to determine where to jump.
In that case, `pc` already shows the following instruction, but since the table shows `begin_offset ... end_offset`, the comparison is done with `begin_offset < pc && pc <= end_offset`.
If there is a corresponding handler, move `pc` to `handler.target_offset` and continue running the VM.
When a global jump across `ensure` is made by `return`, `break`, `next`, `redo` and `retry`, the extended `RBreak` object saves and restores the C-level execution position.
This extended `RBreak` can have tag information, which makes it a pseudo coroutine (the "tag" mimics CRuby).
The implementation of pseudo coroutines by `RBreak` is summarized by `CHECKPOINT_RESTORE ... CHECKPOINT_MAIN ... CHECKPOINT_END` and `throw_tagged_break` / `unwind_ensure` macros.
The restart of processing is branched by `RBREAK_TAG_FOREACH(DISPATCH_CHECKPOINTS)`.
- Not only `rescue` blocks but also `ensure` blocks are now sandwiched between `OP_EXCEPT` and `OP_RAISEIF`.
- Remove the function `ecall()`.
It is no longer necessary to re-enter the VM to perform an "ensure block".
This will resolves #1888.
- Added instruction `OP_JUW` (Jump while UnWind).
It jumps unconditionally like `OP_JMP`, but searches the catch handler table and executes the ensure block.
Since it searches the catch handler table, it is much heavier than `OP_JMP`.
Diffstat (limited to 'include')
| -rw-r--r-- | include/mruby.h | 8 | ||||
| -rw-r--r-- | include/mruby/error.h | 33 | ||||
| -rw-r--r-- | include/mruby/ops.h | 1 |
3 files changed, 34 insertions, 8 deletions
diff --git a/include/mruby.h b/include/mruby.h index c9425e8a7..9f1d1aa30 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -149,8 +149,6 @@ typedef struct { mrb_sym mid; const struct RProc *proc; mrb_value *stackent; - uint16_t ridx; - uint16_t epos; struct REnv *env; const mrb_code *pc; /* return address */ const mrb_code *err; /* error position */ @@ -177,11 +175,6 @@ struct mrb_context { mrb_callinfo *ci; mrb_callinfo *cibase, *ciend; - uint16_t *rescue; /* exception handler stack */ - uint16_t rsize; - struct RProc **ensure; /* ensure handler stack */ - uint16_t esize, eidx; - enum mrb_fiber_state status : 4; mrb_bool vmexec : 1; struct RFiber *fib; @@ -301,7 +294,6 @@ typedef struct mrb_state { mrb_atexit_func *atexit_stack; #endif uint16_t atexit_stack_len; - uint16_t ecall_nest; /* prevent infinite recursive ecall() */ } mrb_state; /** diff --git a/include/mruby/error.h b/include/mruby/error.h index 72f6c5f3d..67a0a539e 100644 --- a/include/mruby/error.h +++ b/include/mruby/error.h @@ -66,6 +66,39 @@ mrb_break_value_set(struct RBreak *brk, mrb_value val) #define mrb_break_proc_get(brk) ((brk)->proc) #define mrb_break_proc_set(brk, p) ((brk)->proc = p) +#define RBREAK_TAG_FOREACH(f) \ + f(RBREAK_TAG_BREAK, 0) \ + f(RBREAK_TAG_BREAK_UPPER, 1) \ + f(RBREAK_TAG_BREAK_INTARGET, 2) \ + f(RBREAK_TAG_RETURN_BLOCK, 3) \ + f(RBREAK_TAG_RETURN, 4) \ + f(RBREAK_TAG_RETURN_TOPLEVEL, 5) \ + f(RBREAK_TAG_JUMP, 6) \ + f(RBREAK_TAG_STOP, 7) + +#define RBREAK_TAG_DEFINE(tag, i) tag = i, +enum { + RBREAK_TAG_FOREACH(RBREAK_TAG_DEFINE) +}; +#undef RBREAK_TAG_DEFINE + +#define RBREAK_TAG_BIT 3 +#define RBREAK_TAG_BIT_OFF 8 +#define RBREAK_TAG_MASK (~(~UINT32_C(0) << RBREAK_TAG_BIT)) + +static inline uint32_t +mrb_break_tag_get(struct RBreak *brk) +{ + return (brk->flags >> RBREAK_TAG_BIT_OFF) & RBREAK_TAG_MASK; +} + +static inline void +mrb_break_tag_set(struct RBreak *brk, uint32_t tag) +{ + brk->flags &= ~(RBREAK_TAG_MASK << RBREAK_TAG_BIT_OFF); + brk->flags |= (tag & RBREAK_TAG_MASK) << RBREAK_TAG_BIT_OFF; +} + /** * Protect * diff --git a/include/mruby/ops.h b/include/mruby/ops.h index 8ff44088e..770e54f3d 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -49,6 +49,7 @@ OPCODE(JMP, S) /* pc=a */ OPCODE(JMPIF, BS) /* if R(a) pc=b */ OPCODE(JMPNOT, BS) /* if !R(a) pc=b */ OPCODE(JMPNIL, BS) /* if R(a)==nil pc=b */ +OPCODE(JUW, S) /* unwind_and_jump_to(a) */ OPCODE(EXCEPT, B) /* R(a) = exc */ OPCODE(RESCUE, BB) /* R(b) = R(a).isa?(R(b)) */ OPCODE(RAISEIF, B) /* raise(R(a)) if R(a) */ |
