summaryrefslogtreecommitdiffhomepage
path: root/mrbgems/mruby-compiler/core/codegen.c
diff options
context:
space:
mode:
authordearblue <[email protected]>2020-07-18 19:05:12 +0900
committerYukihiro "Matz" Matsumoto <[email protected]>2020-10-12 16:21:33 +0900
commitc1f112c49a597da8478516261f0812bc073f6cd8 (patch)
treec5f7f8d805f7bf767a06a10e9b7ef82071cfcee2 /mrbgems/mruby-compiler/core/codegen.c
parent0d7b4deccf445e3edae9239bf40b91cc79e83634 (diff)
downloadmruby-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 'mrbgems/mruby-compiler/core/codegen.c')
-rw-r--r--mrbgems/mruby-compiler/core/codegen.c91
1 files changed, 35 insertions, 56 deletions
diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c
index 7911ecb36..b3ce388a9 100644
--- a/mrbgems/mruby-compiler/core/codegen.c
+++ b/mrbgems/mruby-compiler/core/codegen.c
@@ -40,7 +40,6 @@ enum looptype {
struct loopinfo {
enum looptype type;
int pc0, pc1, pc2, pc3, acc;
- int ensure_level;
struct loopinfo *prev;
};
@@ -61,7 +60,6 @@ typedef struct scope {
mrb_bool mscope:1;
struct loopinfo *loop;
- int ensure_level;
mrb_sym filename_sym;
uint16_t lineno;
@@ -1465,16 +1463,19 @@ codegen(codegen_scope *s, node *tree, int val)
{
int noexc, exend, pos1, pos2, tmp;
struct loopinfo *lp;
+ int catch_entry, begin, end;
if (tree->car == NULL) goto exit;
lp = loop_push(s, LOOP_BEGIN);
lp->pc0 = new_label(s);
- lp->pc1 = genjmp(s, OP_ONERR, 0);
+ catch_entry = catch_hander_new(s);
+ begin = s->pc;
codegen(s, tree->car, VAL);
pop();
lp->type = LOOP_RESCUE;
+ end = s->pc;
noexc = genjmp(s, OP_JMP, 0);
- dispatch(s, lp->pc1);
+ catch_hander_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
tree = tree->cdr;
exend = 0;
pos1 = 0;
@@ -1539,7 +1540,6 @@ codegen(codegen_scope *s, node *tree, int val)
pop();
tree = tree->cdr;
dispatch(s, noexc);
- genop_1(s, OP_POPERR, 1);
if (tree->car) {
codegen(s, tree->car, val);
}
@@ -1555,14 +1555,22 @@ codegen(codegen_scope *s, node *tree, int val)
if (!tree->cdr || !tree->cdr->cdr ||
(nint(tree->cdr->cdr->car) == NODE_BEGIN &&
tree->cdr->cdr->cdr)) {
+ int catch_entry, begin, end, target;
int idx;
- s->ensure_level++;
- idx = scope_body(s, tree->cdr, NOVAL);
- genop_1(s, OP_EPUSH, idx);
+ catch_entry = catch_hander_new(s);
+ begin = s->pc;
codegen(s, tree->car, val);
- s->ensure_level--;
- genop_1(s, OP_EPOP, 1);
+ end = target = s->pc;
+ push();
+ idx = cursp();
+ genop_1(s, OP_EXCEPT, idx);
+ push();
+ codegen(s, tree->cdr->cdr, NOVAL);
+ pop();
+ genop_1(s, OP_RAISEIF, idx);
+ pop();
+ catch_hander_set(s, catch_entry, MRB_CATCH_ENSURE, begin, end, target);
}
else { /* empty ensure ignored */
codegen(s, tree->car, val);
@@ -2024,18 +2032,20 @@ codegen(codegen_scope *s, node *tree, int val)
if ((len == 2 && name[0] == '|' && name[1] == '|') &&
(nint(tree->car->car) == NODE_CONST ||
nint(tree->car->car) == NODE_CVAR)) {
- int onerr, noexc, exc;
+ int catch_entry, begin, end;
+ int noexc, exc;
struct loopinfo *lp;
- onerr = genjmp(s, OP_ONERR, 0);
lp = loop_push(s, LOOP_BEGIN);
- lp->pc1 = onerr;
+ lp->pc0 = new_label(s);
+ catch_entry = catch_hander_new(s);
+ begin = s->pc;
exc = cursp();
codegen(s, tree->car, VAL);
- lp->type = LOOP_RESCUE;
- genop_1(s, OP_POPERR, 1);
+ end = s->pc;
noexc = genjmp(s, OP_JMP, 0);
- dispatch(s, onerr);
+ lp->type = LOOP_RESCUE;
+ catch_hander_set(s, catch_entry, MRB_CATCH_RESCUE, begin, end, s->pc);
genop_1(s, OP_EXCEPT, exc);
genop_1(s, OP_LOADF, exc);
dispatch(s, noexc);
@@ -2288,11 +2298,8 @@ codegen(codegen_scope *s, node *tree, int val)
raise_error(s, "unexpected next");
}
else if (s->loop->type == LOOP_NORMAL) {
- if (s->ensure_level > s->loop->ensure_level) {
- genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
- }
codegen(s, tree, NOVAL);
- genjmp(s, OP_JMP, s->loop->pc0);
+ genjmp(s, OP_JUW, s->loop->pc0);
}
else {
if (tree) {
@@ -2312,10 +2319,7 @@ codegen(codegen_scope *s, node *tree, int val)
raise_error(s, "unexpected redo");
}
else {
- if (s->ensure_level > s->loop->ensure_level) {
- genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
- }
- genjmp(s, OP_JMP, s->loop->pc2);
+ genjmp(s, OP_JUW, s->loop->pc2);
}
if (val) push();
break;
@@ -2323,32 +2327,16 @@ codegen(codegen_scope *s, node *tree, int val)
case NODE_RETRY:
{
const char *msg = "unexpected retry";
+ const struct loopinfo *lp = s->loop;
- if (!s->loop) {
+ while (lp && lp->type != LOOP_RESCUE) {
+ lp = lp->prev;
+ }
+ if (!lp) {
raise_error(s, msg);
}
else {
- struct loopinfo *lp = s->loop;
- int n = 0;
-
- while (lp && lp->type != LOOP_RESCUE) {
- if (lp->type == LOOP_BEGIN) {
- n++;
- }
- lp = lp->prev;
- }
- if (!lp) {
- raise_error(s, msg);
- }
- else {
- if (n > 0) {
- genop_1(s, OP_POPERR, n);
- }
- if (s->ensure_level > lp->ensure_level) {
- genop_1(s, OP_EPOP, s->ensure_level - lp->ensure_level);
- }
- genjmp(s, OP_JMP, lp->pc0);
- }
+ genjmp(s, OP_JUW, lp->pc0);
}
if (val) push();
}
@@ -3132,7 +3120,6 @@ loop_push(codegen_scope *s, enum looptype t)
p->type = t;
p->pc0 = p->pc1 = p->pc2 = p->pc3 = 0;
p->prev = s->loop;
- p->ensure_level = s->ensure_level;
p->acc = cursp();
s->loop = p;
@@ -3148,7 +3135,6 @@ loop_break(codegen_scope *s, node *tree)
}
else {
struct loopinfo *loop;
- int n = 0;
if (tree) {
gen_retval(s, tree);
@@ -3157,7 +3143,6 @@ loop_break(codegen_scope *s, node *tree)
loop = s->loop;
while (loop) {
if (loop->type == LOOP_BEGIN) {
- n++;
loop = loop->prev;
}
else if (loop->type == LOOP_RESCUE) {
@@ -3171,20 +3156,14 @@ loop_break(codegen_scope *s, node *tree)
raise_error(s, "unexpected break");
return;
}
- if (n > 0) {
- genop_1(s, OP_POPERR, n);
- }
if (loop->type == LOOP_NORMAL) {
int tmp;
- if (s->ensure_level > s->loop->ensure_level) {
- genop_1(s, OP_EPOP, s->ensure_level - s->loop->ensure_level);
- }
if (tree) {
gen_move(s, loop->acc, cursp(), 0);
}
- tmp = genjmp(s, OP_JMP, loop->pc3);
+ tmp = genjmp(s, OP_JUW, loop->pc3);
loop->pc3 = tmp;
}
else {