From fb5d966a45b321ed4d646b0471881afa0a6bebb5 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 20 Dec 2025 22:37:30 +0900 Subject: [PATCH] mruby-compiler: optimize masgn to generate literals directly into target registers When all lhs are local variables and all rhs are simple literals (integers, nil, true, false), generate values directly into target registers instead of using temporaries and MOVE instructions. For example, `a,b = 1,2` now generates: LOADI_1 R1 LOADI_2 R2 instead of: LOADI_1 R3 LOADI_2 R4 MOVE R1 R3 MOVE R2 R4 Co-authored-by: Claude --- mrbgems/mruby-compiler/core/codegen.c | 83 +++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index de52f87af..74ba05cbb 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -2661,6 +2661,58 @@ nosplat(node *t) return TRUE; } +/* Check if node is a simple literal that can be generated into any register */ +static mrb_bool +is_simple_literal(node *n) +{ + switch (get_node_type(n)) { + case NODE_INT: + case NODE_NIL: + case NODE_TRUE: + case NODE_FALSE: + return TRUE; + default: + return FALSE; + } +} + +/* Check if all lhs are local variables and get their registers */ +static mrb_bool +all_lvar_pre(codegen_scope *s, node *pre, int *regs, int max) +{ + int i = 0; + while (pre && i < max) { + if (get_node_type(pre->car) != NODE_LVAR) return FALSE; + int idx = lv_idx(s, var_node(pre->car)->symbol); + if (idx <= 0) return FALSE; /* not a local variable */ + regs[i++] = idx; + pre = pre->cdr; + } + return pre == NULL; /* all processed */ +} + +/* Generate a simple literal directly into a specific register */ +static void +gen_literal_to_reg(codegen_scope *s, node *n, int reg) +{ + switch (get_node_type(n)) { + case NODE_INT: + gen_int(s, reg, int_node(n)->value); + break; + case NODE_NIL: + genop_1(s, OP_LOADNIL, reg); + break; + case NODE_TRUE: + genop_1(s, OP_LOADT, reg); + break; + case NODE_FALSE: + genop_1(s, OP_LOADF, reg); + break; + default: + break; + } +} + static mrb_sym attrsym(codegen_scope *s, mrb_sym a) { @@ -4974,6 +5026,37 @@ codegen_masgn(codegen_scope *s, node *varnode, node *rhs, int sp, int val) if (an->elements && nosplat(an->elements)) { /* fixed rhs */ t = an->elements; + + /* Optimization: direct generation for simple cases */ + /* When all lhs are local vars and all rhs are simple literals, */ + /* generate directly into target registers (no temporaries) */ + if (masgn_n->pre && !masgn_n->rest && !masgn_n->post) { + int regs[16]; /* support up to 16 variables */ + node *lhs = masgn_n->pre; + node *rhs_elem = t; + int count = 0; + mrb_bool all_simple = TRUE; + + /* Count and check rhs are all simple literals */ + while (rhs_elem && count < 16) { + if (!is_simple_literal(rhs_elem->car)) { + all_simple = FALSE; + break; + } + count++; + rhs_elem = rhs_elem->cdr; + } + if (all_simple && count > 0 && all_lvar_pre(s, lhs, regs, count)) { + /* Direct generation: generate literals into target registers */ + rhs_elem = t; + for (int i = 0; i < count; i++) { + gen_literal_to_reg(s, rhs_elem->car, regs[i]); + rhs_elem = rhs_elem->cdr; + } + return; + } + } + rhs_reg = cursp(); /* Save register where values will be pushed */ while (t) { codegen(s, t->car, VAL);