From fc4b4f2e8db3d68b80b9c7580c40a0165736006c Mon Sep 17 00:00:00 2001 From: Jeremy Evans Date: Wed, 20 Jul 2022 14:06:28 -0700 Subject: Expand newarray/expandarray optimization for unequal operands This optimizes unbalanced multiple assignment cases such as: ```ruby a.b, c.d = e, f, g a.b, c.d, e.f = g, h ``` Previously, this would use: ``` newarray(3) expandarray(2, 0) newarray(2) expandarray(3, 0) ``` These would both allocate arrays. This switches to opt_reverse with either pop or putnil: ``` pop opt_reverse(2) putnil opt_reverse(3) ``` This avoids an unnecessary array allocation, and results in a 35-76% performance increase in these types of unbalanced cases (tested with benchmark/masgn.yml). --- compile.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/compile.c b/compile.c index 9716f04374..8879e661fe 100644 --- a/compile.c +++ b/compile.c @@ -3335,16 +3335,20 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal if (IS_INSN_ID(iobj, newarray)) { LINK_ELEMENT *next = iobj->link.next; if (IS_INSN(next) && IS_INSN_ID(next, expandarray) && - OPERAND_AT(iobj, 0) == OPERAND_AT(next, 0) && OPERAND_AT(next, 1) == INT2FIX(0)) { - ELEM_REMOVE(next); + VALUE op1, op2; + op1 = OPERAND_AT(iobj, 0); + op2 = OPERAND_AT(next, 0); + ELEM_REMOVE(next); + + if (op1 == op2) { /* * newarray 2 * expandarray 2, 0 * => * swap */ - if (OPERAND_AT(iobj, 0) == INT2FIX(2)) { + if (op1 == INT2FIX(2)) { INSN_OF(iobj) = BIN(swap); iobj->operand_size = 0; } @@ -3357,6 +3361,38 @@ iseq_peephole_optimize(rb_iseq_t *iseq, LINK_ELEMENT *list, const int do_tailcal else { INSN_OF(iobj) = BIN(opt_reverse); } + } + else { + NODE dummy_line_node = generate_dummy_line_node(iobj->insn_info.line_no, iobj->insn_info.node_id); + long diff = FIX2LONG(op1) - FIX2LONG(op2); + INSN_OF(iobj) = BIN(opt_reverse); + OPERAND_AT(iobj, 0) = OPERAND_AT(next, 0); + + if (op1 > op2) { + /* X > Y + * newarray X + * expandarray Y, 0 + * => + * pop * (Y-X) + * opt_reverse Y + */ + for (; diff > 0; diff--) { + INSERT_BEFORE_INSN(iobj, &dummy_line_node, pop); + } + } + else { /* (op1 < op2) */ + /* X < Y + * newarray X + * expandarray Y, 0 + * => + * putnil * (Y-X) + * opt_reverse Y + */ + for (; diff < 0; diff++) { + INSERT_BEFORE_INSN(iobj, &dummy_line_node, putnil); + } + } + } } } -- cgit v1.2.3