From: Sergey Bronnikov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Kaplun <skaplun@tarantool.org>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit 3/3] DUALNUM: Improve/fix edge cases of unary minus.
Date: Wed, 4 Mar 2026 12:27:10 +0300 [thread overview]
Message-ID: <f16c206b-65bf-4286-b8b6-4b62dd35adc2@tarantool.org> (raw)
In-Reply-To: <cf79020b3bf3b06d5f2dcedb154de2fcd6fcb950.1772437706.git.skaplun@tarantool.org>
[-- Attachment #1: Type: text/plain, Size: 9694 bytes --]
Hi, Sergey,
thanks for the patch! LGTM
Sergey
On 3/2/26 10:52, Sergey Kaplun wrote:
> From: Mike Pall <mike>
>
> Thanks to Sergey Kaplun.
>
> (cherry picked from commit 707c12bf00dafdfd3899b1a6c36435dbbf6c7022)
>
> In a single-number build (default for x86/x64), `-(0)` is always `-0`
> due to its number type. However, in DUALNUM mode, the `0` may have the
> integer type, and for this integer, `BC_UNM` has no effect. `0` may be
> represented via number or integer type, where the resulting `0` is
> constructed during initial parsing (`lj_strscan_scan()`) or arithmetic
> operations (`BC_MODVV`). This inconsistency may break the JIT semantics
> and lead to the assertion failure in the `lj_check_slots()`.
>
> This patch fixes the incorrect `BC_UNM` for integer operand 0. Also, the
> narrowing optimization for the unary minus should be more strict to
> avoid incorrect narrowing for integer slots and make JIT intact with the
> VM semantics.
>
> Sergey Kaplun:
> * added the description and the test for the problem
>
> Part of tarantool/tarantool#12134
> ---
> src/lj_opt_narrow.c | 5 +-
> src/vm_arm.dasc | 5 +-
> src/vm_arm64.dasc | 2 +
> src/vm_mips.dasc | 6 ++-
> src/vm_mips64.dasc | 6 ++-
> src/vm_ppc.dasc | 7 ++-
> src/vm_x64.dasc | 4 ++
> src/vm_x86.dasc | 5 ++
> .../lj-1422-incorrect-unm-for-mzero.test.lua | 54 +++++++++++++++++++
> .../tarantool-tests/lj-1422-unm-zero.test.lua | 12 +++++
> 10 files changed, 99 insertions(+), 7 deletions(-)
> create mode 100644 test/tarantool-tests/lj-1422-incorrect-unm-for-mzero.test.lua
> create mode 100644 test/tarantool-tests/lj-1422-unm-zero.test.lua
>
> diff --git a/src/lj_opt_narrow.c b/src/lj_opt_narrow.c
> index 6e3e9533..3bf7320d 100644
> --- a/src/lj_opt_narrow.c
> +++ b/src/lj_opt_narrow.c
> @@ -553,10 +553,9 @@ TRef lj_opt_narrow_unm(jit_State *J, TRef rc, TValue *vc)
> rc = conv_str_tonum(J, rc, vc);
> if (tref_isinteger(rc)) {
> uint32_t k = (uint32_t)numberVint(vc);
> - if ((tvisint(vc) || k != 0) && k != 0x80000000u) {
> + if (k != 0 && k != 0x80000000u) {
> TRef zero = lj_ir_kint(J, 0);
> - if (!tvisint(vc))
> - emitir(IRTGI(IR_NE), rc, zero);
> + emitir(IRTGI(IR_NE), rc, zero);
> return emitir(IRTGI(IR_SUBOV), zero, rc);
> }
> rc = emitir(IRTN(IR_CONV), rc, IRCONV_NUM_INT);
> diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc
> index 7095e660..785d13ac 100644
> --- a/src/vm_arm.dasc
> +++ b/src/vm_arm.dasc
> @@ -2930,13 +2930,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | bhi ->vmeta_unm
> | eorne CARG2, CARG2, #0x80000000
> | bne >5
> - | rsbseq CARG1, CARG1, #0
> + | rsbs CARG1, CARG1, #0
> + | ldrdeq CARG12, >8
> | ldrdvs CARG12, >9
> |5:
> | strd CARG12, [BASE, RA]
> | ins_next3
> |
> |.align 8
> + |8:
> + | .long 0x00000000, 0x80000000 // -0.
> |9:
> | .long 0x00000000, 0x41e00000 // 2^31.
> break;
> diff --git a/src/vm_arm64.dasc b/src/vm_arm64.dasc
> index 6600e226..c64b736b 100644
> --- a/src/vm_arm64.dasc
> +++ b/src/vm_arm64.dasc
> @@ -2481,6 +2481,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | movz CARG3, #0x41e0, lsl #48 // 2^31.
> | add TMP0, TMP0, TISNUM
> | csel TMP0, TMP0, CARG3, vc
> + | movz CARG3, #0x8000, lsl #48 // -0.
> + | csel TMP0, TMP0, CARG3, ne
> |5:
> | str TMP0, [BASE, RA, lsl #3]
> | ins_next
> diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc
> index 32caabf7..dcb04ed8 100644
> --- a/src/vm_mips.dasc
> +++ b/src/vm_mips.dasc
> @@ -3319,7 +3319,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | addu RA, BASE, RA
> | bne SFARG1HI, TISNUM, >2
> |. lw SFARG1LO, LO(RB)
> - | lui TMP1, 0x8000
> + | beqz SFARG1LO, >3
> + |. lui TMP1, 0x8000
> | beq SFARG1LO, TMP1, ->vmeta_unm // Meta handler deals with -2^31.
> |. negu SFARG1LO, SFARG1LO
> |1:
> @@ -3333,6 +3334,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> |. lui TMP1, 0x8000
> | b <1
> |. xor SFARG1HI, SFARG1HI, TMP1
> + |3:
> + | b <1
> + |. lui SFARG1HI, 0x8000 // -0.
> break;
> case BC_LEN:
> | // RA = dst*8, RD = src*8
> diff --git a/src/vm_mips64.dasc b/src/vm_mips64.dasc
> index 44fba36c..34da6473 100644
> --- a/src/vm_mips64.dasc
> +++ b/src/vm_mips64.dasc
> @@ -3563,7 +3563,8 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | sextw CARG1, CARG1
> | beq CARG1, TMP1, ->vmeta_unm // Meta handler deals with -2^31.
> |. negu CARG1, CARG1
> - | zextw CARG1, CARG1
> + | beqz CARG1, >3
> + |. zextw CARG1, CARG1
> | settp CARG1, TISNUM
> |1:
> | ins_next1
> @@ -3575,6 +3576,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> |. dsll TMP1, TMP1, 32
> | b <1
> |. xor CARG1, CARG1, TMP1
> + |3:
> + | b <1
> + |. dsll CARG1, TMP1, 32
> break;
> case BC_LEN:
> | // RA = dst*8, RD = src*8
> diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc
> index 980ad897..88fa7b67 100644
> --- a/src/vm_ppc.dasc
> +++ b/src/vm_ppc.dasc
> @@ -3803,11 +3803,13 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | bne >5
> |.if GPR64
> | lus TMP2, 0x8000
> - | neg TMP0, TMP0
> + | neg. TMP0, TMP0
> + | beq >8
> | cmplw TMP0, TMP2
> | beq >4
> |.else
> | nego. TMP0, TMP0
> + | beq >8
> | bso >4
> |1:
> |.endif
> @@ -3834,6 +3836,9 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | stw TMP0, 4(RA)
> |.if DUALNUM
> | b <3
> + |8:
> + | lus TMP1, 0x8000 // -0.
> + | b <7
> |.else
> | ins_next2
> |.endif
> diff --git a/src/vm_x64.dasc b/src/vm_x64.dasc
> index 8b6781a6..ee669887 100644
> --- a/src/vm_x64.dasc
> +++ b/src/vm_x64.dasc
> @@ -3179,11 +3179,15 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> |.if DUALNUM
> | checkint RB, >5
> | neg RBd
> + | jz >3
> | jo >4
> | setint RB
> |9:
> | mov [BASE+RA*8], RB
> | ins_next
> + |3:
> + | mov64 RB, U64x(80000000,00000000) // -0.
> + | jmp <9
> |4:
> | mov64 RB, U64x(41e00000,00000000) // 2^31.
> | jmp <9
> diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc
> index 7c11c78e..027bdcd6 100644
> --- a/src/vm_x86.dasc
> +++ b/src/vm_x86.dasc
> @@ -3780,11 +3780,16 @@ static void build_ins(BuildCtx *ctx, BCOp op, int defop)
> | checkint RD, >5
> | mov RB, [BASE+RD*8]
> | neg RB
> + | jz >3
> | jo >4
> | mov dword [BASE+RA*8+4], LJ_TISNUM
> + |8:
> | mov dword [BASE+RA*8], RB
> |9:
> | ins_next
> + |3:
> + | mov dword [BASE+RA*8+4], 0x80000000 // -0.
> + | jmp <8
> |4:
> | mov dword [BASE+RA*8+4], 0x41e00000 // 2^31.
> | mov dword [BASE+RA*8], 0
> diff --git a/test/tarantool-tests/lj-1422-incorrect-unm-for-mzero.test.lua b/test/tarantool-tests/lj-1422-incorrect-unm-for-mzero.test.lua
> new file mode 100644
> index 00000000..7046784c
> --- /dev/null
> +++ b/test/tarantool-tests/lj-1422-incorrect-unm-for-mzero.test.lua
> @@ -0,0 +1,54 @@
> +local tap = require('tap')
> +
> +-- This test demonstrates LuaJIT's inconsistencies in the VM and
> +-- the JIT engine in the DUALNUM mode for 0.
> +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1422.
> +
> +local test = tap.test('lj-1422-incorrect-unm-for-mzero'):skipcond({
> + ['Test requires JIT enabled'] = not jit.status(),
> +})
> +
> +test:plan(3)
> +
> +local tonumber = tonumber
> +local tostring = tostring
> +
> +local function always_number(val)
> + return tonumber(val) or 1
> +end
> +
> +-- Yielded int type for non-x86 arches.
> +local function modvn(v1)
> + return always_number(v1) % 1
> +end
> +
> +local function unm(v)
> + return -v
> +end
> +
> +jit.opt.start('hotloop=1', 'hotexit=1')
> +
> +always_number(nil) -- Root trace.
> +always_number(nil)
> +
> +local stack_slot = nil
> +for _ = 1, 5 do
> + always_number(0) -- Compile side trace.
> + -- The side trace crashes in the `rec_check_slots()` for non-x86
> + -- arches before the patch.
> + stack_slot = tostring(unm(modvn(stack_slot)))
> +end
> +
> +test:is(stack_slot, '-0', 'correct result of the trace execution')
> +
> +-- `tonumber()` recording and conversion to number.
> +local results = {nil, nil, nil, nil}
> +for i = 1, 4 do
> + local slot = -tonumber('0')
> + results[i] = tostring(slot)
> +end
> +
> +test:is(results[1], '-0', 'correct result of the expression')
> +test:samevalues(results, 'correct result of the tonumber recording')
> +
> +test:done(true)
> diff --git a/test/tarantool-tests/lj-1422-unm-zero.test.lua b/test/tarantool-tests/lj-1422-unm-zero.test.lua
> new file mode 100644
> index 00000000..77062418
> --- /dev/null
> +++ b/test/tarantool-tests/lj-1422-unm-zero.test.lua
> @@ -0,0 +1,12 @@
> +local tap = require('tap')
> +
> +-- The test file to demonstrate LuaJIT's incorrect BC_UNM for the
> +-- 0 operand for the DUALNUM mode.
> +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1422.
> +
> +local test = tap.test('lj-1422-unm-zero')
> +test:plan(1)
> +
> +test:ok(tostring(-0) == '-0', 'correct unary minus for 0')
> +
> +test:done(true)
[-- Attachment #2: Type: text/html, Size: 9808 bytes --]
prev parent reply other threads:[~2026-03-04 9:27 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-02 7:52 [Tarantool-patches] [PATCH luajit 0/3] Narrowing unary minus dualnum Sergey Kaplun via Tarantool-patches
2026-03-02 7:52 ` [Tarantool-patches] [PATCH luajit 1/3] Add ffi.abi("dualnum") Sergey Kaplun via Tarantool-patches
2026-03-03 18:09 ` Sergey Bronnikov via Tarantool-patches
2026-03-02 7:52 ` [Tarantool-patches] [PATCH luajit 2/3] DUALNUM: Fix narrowing of unary minus Sergey Kaplun via Tarantool-patches
2026-03-04 7:37 ` Sergey Bronnikov via Tarantool-patches
2026-03-04 10:34 ` Sergey Kaplun via Tarantool-patches
2026-03-02 7:52 ` [Tarantool-patches] [PATCH luajit 3/3] DUALNUM: Improve/fix edge cases " Sergey Kaplun via Tarantool-patches
2026-03-04 9:27 ` Sergey Bronnikov via Tarantool-patches [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=f16c206b-65bf-4286-b8b6-4b62dd35adc2@tarantool.org \
--to=tarantool-patches@dev.tarantool.org \
--cc=sergeyb@tarantool.org \
--cc=skaplun@tarantool.org \
--subject='Re: [Tarantool-patches] [PATCH luajit 3/3] DUALNUM: Improve/fix edge cases of unary minus.' \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox