From 0b1af858e2c8b7bbdc45c41df24be200e2fe27f2 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Fri, 23 Jan 2026 00:33:25 +0900 Subject: [PATCH] vm: add OP_RETNIL for returning nil directly Add a new opcode that returns nil without requiring LOADNIL + RETURN. This avoids loading nil into a register by setting the return value (v) directly. The implementation uses a separate label (L_RETURN_NIL) to bypass v = regs[a], preserving self in regs[0] for ensure blocks. Codegen applies peephole optimization to fuse LOADNIL + RETURN -> RETNIL. Co-authored-by: Claude --- NEWS.md | 1 + include/mruby/ops.h | 1 + mrbgems/mruby-compiler/core/codegen.c | 7 ++++++- src/codedump.c | 3 +++ src/vm.c | 8 ++++++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index b5f7ef5b7..01d6f90ed 100644 --- a/NEWS.md +++ b/NEWS.md @@ -85,6 +85,7 @@ New super-instructions that fuse common opcode sequences to reduce bytecode size - `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) +- `OP_RETNIL`: Single-byte instruction for `return nil` pattern ([96641c9](https://github.com/mruby/mruby/commit/96641c9)) # Fixed GitHub Issues diff --git a/include/mruby/ops.h b/include/mruby/ops.h index 5bb05f735..60ff1266d 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -74,6 +74,7 @@ OPCODE(KARG, BB) /* R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) */ OPCODE(RETURN, B) /* return R[a] (normal) */ OPCODE(RETURN_BLK, B) /* return R[a] (in-block return) */ OPCODE(RETSELF, Z) /* return self */ +OPCODE(RETNIL, Z) /* return nil */ OPCODE(BREAK, B) /* break R[a] */ OPCODE(BLKPUSH, BS) /* R[a] = block (16=m5:r1:m5:d1:lv4) */ OPCODE(ADD, B) /* R[a] = R[a]+R[a+1] */ diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 1eba308f1..8b2d27a59 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1151,7 +1151,12 @@ gen_return(codegen_scope *s, uint8_t op, uint16_t src) rewind_pc(s); genop_0(s, OP_RETSELF); } - else if (data.insn != OP_RETURN && data.insn != OP_RETSELF) { + else if (data.insn == OP_LOADNIL && src == data.a && op == OP_RETURN) { + /* LOADNIL + RETURN -> RETNIL */ + rewind_pc(s); + genop_0(s, OP_RETNIL); + } + else if (data.insn != OP_RETURN && data.insn != OP_RETSELF && data.insn != OP_RETNIL) { genop_1(s, op, src); } } diff --git a/src/codedump.c b/src/codedump.c index fa21e8150..d5c882529 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -399,6 +399,9 @@ codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out) CASE(OP_RETSELF, Z): fprintf(out, "RETSELF\n"); break; + CASE(OP_RETNIL, Z): + fprintf(out, "RETNIL\n"); + break; CASE(OP_BREAK, B): fprintf(out, "BREAK\t\tR%d\t", a); print_lv_a(mrb, irep, a, out); diff --git a/src/vm.c b/src/vm.c index 9d2f2a87b..bce231f74 100644 --- a/src/vm.c +++ b/src/vm.c @@ -2744,6 +2744,10 @@ RETRY_TRY_BLOCK: a = 0; goto NORMAL_RETURN; } + CASE(OP_RETNIL, Z) { + a = 0; + goto L_RETURN_NIL; + } CASE(OP_RETURN, B) { mrb_int acc; mrb_value v; @@ -2751,6 +2755,10 @@ RETRY_TRY_BLOCK: NORMAL_RETURN: v = regs[a]; + goto L_RETURN; + L_RETURN_NIL: + v = mrb_nil_value(); + L_RETURN: mrb_gc_protect(mrb, v); return_ci = ci; CHECKPOINT_RESTORE(RBREAK_TAG_BREAK) {