diff --git a/NEWS.md b/NEWS.md index a8f3e980a..b5f7ef5b7 100644 --- a/NEWS.md +++ b/NEWS.md @@ -84,6 +84,7 @@ New super-instructions that fuse common opcode sequences to reduce bytecode size - `OP_ADDILV`/`OP_SUBILV`: Local variable increment/decrement fusion for `i += n` patterns ([43f64b9](https://github.com/mruby/mruby/commit/43f64b9)) - `OP_RETSELF`: Single-byte instruction for `return self` pattern ([a71db8c](https://github.com/mruby/mruby/commit/a71db8c)) - `OP_MATCHERR`: Pattern matching error with conditional execution for `in` patterns ([944168a](https://github.com/mruby/mruby/commit/944168a), [e9a9ba4](https://github.com/mruby/mruby/commit/e9a9ba4)) +- `OP_BLKCALL`: Direct block call for `yield`, bypassing method dispatch (13-17% faster) # Fixed GitHub Issues diff --git a/include/mruby/ops.h b/include/mruby/ops.h index 51b8a4da0..5bb05f735 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -64,6 +64,7 @@ OPCODE(SSENDB, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n OPCODE(SEND, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */ OPCODE(SENDB, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */ OPCODE(CALL, Z) /* self.call(*, **, &) (But overlay the current call frame; tailcall) */ +OPCODE(BLKCALL, BB) /* R[a] = R[a].call(R[a+1],... ,R[a+b]); direct block call */ OPCODE(SUPER, BB) /* R[a] = super(R[a+1],... ,R[a+b+1]) */ OPCODE(ARGARY, BS) /* R[a] = argument array (16=m5:r1:m5:d1:lv4) */ OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */ diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 080380839..1eba308f1 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -5582,7 +5582,14 @@ codegen_yield(codegen_scope *s, node *varnode, int val) pop_n(n + (nk == 15 ? 1 : nk * 2) + 1); genop_2S(s, OP_BLKPUSH, cursp(), (ainfo<<4)|(lv & 0xf)); if (sendv) n = CALL_MAXARGS; - genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_SYM_2(s->mrb, call)), n|(nk<<4)); + if (nk == 0 && n < 15) { + /* fast path: direct block call without method dispatch */ + genop_2(s, OP_BLKCALL, cursp(), n); + } + else { + /* fallback: use SEND for keyword args or splat */ + genop_3(s, OP_SEND, cursp(), sym_idx(s, MRB_SYM_2(s->mrb, call)), n|(nk<<4)); + } if (val) push(); } diff --git a/src/codedump.c b/src/codedump.c index 308dee72f..fa21e8150 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -351,6 +351,9 @@ codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out) CASE(OP_CALL, Z): fprintf(out, "CALL\n"); break; + CASE(OP_BLKCALL, BB): + fprintf(out, "BLKCALL\t\tR%d\t%d\n", a, b); + break; CASE(OP_SUPER, BB): fprintf(out, "SUPER\t\tR%d\t", a); print_args(b, out); diff --git a/src/vm.c b/src/vm.c index d50f7232b..abc31bb3b 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2380,6 +2380,57 @@ RETRY_TRY_BLOCK: JUMP; } + CASE(OP_BLKCALL, BB) { + /* Direct block call: R[a] = R[a].call(R[a+1],...,R[a+b]) */ + /* Skip method dispatch - directly invoke the proc */ + mrb_value recv = regs[a]; + const struct RProc *p; + + if (mrb_unlikely(!mrb_proc_p(recv))) { + mrb_raisef(mrb, E_TYPE_ERROR, "wrong type %T (expected Proc)", recv); + } + p = mrb_proc_ptr(recv); + + /* push callinfo */ + ci = cipush(mrb, a, CINFO_DIRECT, NULL, NULL, NULL, 0, b); + ci->cci = CINFO_NONE; /* mark as VM-to-VM call for proper break handling */ + + /* handle alias */ + MRB_PROC_RESOLVE_ALIAS(ci, p); + if (MRB_PROC_ENV_P(p)) { + ci->mid = MRB_PROC_ENV(p)->mid; + } + ci->u.target_class = MRB_PROC_TARGET_CLASS(p); + CI_PROC_SET(ci, p); + + if (MRB_PROC_CFUNC_P(p)) { + recv = MRB_PROC_CFUNC(p)(mrb, recv); + mrb_gc_arena_shrink(mrb, ai); + if (mrb_unlikely(mrb->exc)) goto L_RAISE; + ci = cipop(mrb); + ci[1].stack[0] = recv; + irep = ci->proc->body.irep; + } + else { + irep = p->body.irep; + if (!irep) { + ci->stack[0] = mrb_nil_value(); + a = 0; + goto L_OP_RETURN_BODY; + } + mrb_int nargs = b + 1; /* args + self */ + if (nargs < irep->nregs) { + stack_extend(mrb, irep->nregs); + stack_clear(regs+nargs, irep->nregs-nargs); + } + if (MRB_PROC_ENV_P(p)) { + regs[0] = MRB_PROC_ENV(p)->stack[0]; + } + ci->pc = irep->iseq; + } + JUMP; + } + CASE(OP_SUPER, BB) { mrb_value recv; struct RClass* target_class = CI_TARGET_CLASS(ci);