mruby-compiler: do not flip + and - opcode for negative literal

The peephole in gen_addsub() rewrote `q + -n` into OP_SUBI n (and
`q - -n` into OP_ADDI n) by negating n. For numeric receivers this is
equivalent, but for receivers overriding + or - the runtime fallback
dispatches the flipped method, losing the original operator. Restrict
the fold to non-negative immediates; negative falls through to the
normal OP_ADD/OP_SUB path with LOADI of the literal value.

close #2557
ref #2579

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-05-09 19:47:27 +09:00
parent 2e429a034d
commit b4f6450509
2 changed files with 21 additions and 11 deletions
+5 -11
View File
@@ -1457,19 +1457,13 @@ gen_addsub(codegen_scope *s, uint8_t op, uint16_t dst)
struct mrb_insn_data data0 = mrb_decode_insn(mrb_prev_pc(s, data.addr));
mrb_int n0;
if (addr_pc(s, data.addr) == s->lastlabel || !get_int_operand(s, &data0, &n0)) {
/* OP_ADDI/OP_SUBI takes upto 8bits */
if (n > UINT8_MAX || n < -UINT8_MAX) goto normal;
/* Fold to OP_ADDI/OP_SUBI only for non-negative 8-bit n; flipping op
for negative n would change the method sent on user override (#2557). */
if (n < 0 || n > UINT8_MAX) goto normal;
rewind_pc(s);
if (n == 0) return;
if (n > 0) {
if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n);
else genop_2(s, OP_SUBI, dst, (uint16_t)n);
}
else { /* n < 0 */
n = -n;
if (op == OP_ADD) genop_2(s, OP_SUBI, dst, (uint16_t)n);
else genop_2(s, OP_ADDI, dst, (uint16_t)n);
}
if (op == OP_ADD) genop_2(s, OP_ADDI, dst, (uint16_t)n);
else genop_2(s, OP_SUBI, dst, (uint16_t)n);
return;
}
if (op == OP_ADD) {
+16
View File
@@ -383,4 +383,20 @@ qwe]
assert_equal :'{foo bar}', h
end
assert('operator override with negative integer literal', '#2557') do
cls = Class.new {
def +(x); ['add', x]; end
def -(x); ['sub', x]; end
}
q = cls.new
assert_equal ['add', 5], q + 5
assert_equal ['add', -5], q + -5
assert_equal ['sub', 5], q - 5
assert_equal ['sub', -5], q - -5
assert_equal ['add', 500], q + 500
assert_equal ['add', -500], q + -500
assert_equal ['sub', 500], q - 500
assert_equal ['sub', -500], q - -500
end
# Not Implemented ATM assert('Literals Regular expression', '8.7.6.5') do