From 51e8da661400c4b57ce15b3df8ede340be391d16 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Tue, 20 Jan 2026 12:38:05 +0900 Subject: [PATCH] vm: add OP_GETIDX0 for fast array[0] access Fuses MOVE+LOADI_0+GETIDX pattern into single instruction. Saves 4 bytes per arr[0] access (7 bytes -> 3 bytes). Co-authored-by: Claude --- include/mruby/ops.h | 1 + mrbgems/mruby-compiler/core/codegen.c | 10 ++++++++++ src/codedump.c | 3 +++ src/vm.c | 27 +++++++++++++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/include/mruby/ops.h b/include/mruby/ops.h index 4fe79d115..9e4432f4d 100644 --- a/include/mruby/ops.h +++ b/include/mruby/ops.h @@ -48,6 +48,7 @@ OPCODE(SETMCNST, BB) /* R[a+1]::Syms[b] = R[a] */ OPCODE(GETUPVAR, BBB) /* R[a] = uvget(b,c) */ OPCODE(SETUPVAR, BBB) /* uvset(b,c,R[a]) */ OPCODE(GETIDX, B) /* R[a] = R[a][R[a+1]] */ +OPCODE(GETIDX0, BB) /* R[a] = R[b][0]; a+1 for method call */ OPCODE(SETIDX, B) /* R[a][R[a+1]] = R[a+2] */ OPCODE(JMP, S) /* pc+=a */ OPCODE(JMPIF, BS) /* if R[a] pc+=b */ diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index 1c1382519..e0da700e5 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -1554,6 +1554,16 @@ gen_binop(codegen_scope *s, mrb_sym op, uint16_t dst) { if (no_peephole(s)) return FALSE; else if (op == MRB_OPSYM_2(s->mrb, aref)) { + /* GETIDX0 fusion: MOVE dst arr; LOADI_0 dst+1 -> GETIDX0 dst arr */ + struct mrb_insn_data data = mrb_last_insn(s); + if (data.insn == OP_LOADI_0 && data.a == dst+1 && addr_pc(s, data.addr) != s->lastlabel) { + struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr)); + if (data0.insn == OP_MOVE && data0.a == dst && data0.b != dst) { + s->pc = addr_pc(s, data0.addr); + genop_2(s, OP_GETIDX0, dst, data0.b); + return TRUE; + } + } genop_1(s, OP_GETIDX, dst); return TRUE; } diff --git a/src/codedump.c b/src/codedump.c index 02fc59f4d..14191c0f3 100644 --- a/src/codedump.c +++ b/src/codedump.c @@ -303,6 +303,9 @@ codedump(mrb_state *mrb, const mrb_irep *irep, FILE *out) CASE(OP_GETIDX, B): fprintf(out, "GETIDX\tR%d\t(R%d)\n", a, a+1); break; + CASE(OP_GETIDX0, BB): + fprintf(out, "GETIDX0\tR%d\tR%d[0]\n", a, b); + break; CASE(OP_SETIDX, B): fprintf(out, "SETIDX\tR%d\t(R%d)\t(R%d)\n", a, a+1, a+2); break; diff --git a/src/vm.c b/src/vm.c index 3b76d18ab..667d09cf1 100644 --- a/src/vm.c +++ b/src/vm.c @@ -1938,6 +1938,33 @@ RETRY_TRY_BLOCK: goto L_SEND_SYM; } + CASE(OP_GETIDX0, BB) { + mrb_value recv = regs[b]; + enum mrb_vtype tt = mrb_type(recv); + + if (mrb_likely(tt == MRB_TT_ARRAY)) { + struct RArray *ary = mrb_ary_ptr(recv); + if (mrb_unlikely(ary->c != mrb->array_class)) goto getidx0_fallback; + if (ARY_EMBED_P(ary)) { + regs[a] = ARY_EMBED_LEN(ary) > 0 ? ary->as.ary[0] : mrb_nil_value(); + } + else { + regs[a] = ary->as.heap.len > 0 ? ary->as.heap.ptr[0] : mrb_nil_value(); + } + NEXT; + } + else if (tt == MRB_TT_HASH) { + if (mrb_obj_ptr(recv)->c != mrb->hash_class) goto getidx0_fallback; + regs[a] = mrb_hash_get(mrb, recv, mrb_fixnum_value(0)); + NEXT; + } + getidx0_fallback: + regs[a] = recv; + SET_FIXNUM_VALUE(regs[a+1], 0); + mid = MRB_OPSYM(aref); + goto L_SEND_SYM; + } + CASE(OP_SETIDX, B) { mrb_value va = regs[a], vb = regs[a+1], vc = regs[a+2]; switch (mrb_type(va)) {