From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id 0C0121BB8FFE; Fri, 13 Mar 2026 11:52:04 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 0C0121BB8FFE DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1773391924; bh=zwItXJPlhVsrJ0kMwyIn6kGILLRIPreYiC/cb8U+jIA=; h=Date:To:Cc:References:In-Reply-To:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=mDMTDrrWqo4aiXDFljye9w2HKJBBHSG0AGwPF39nljYBZaq5G2jWHp0ocbRe/sT0A wFMiCM4jqlvAQpCIaC4Zv6huKOrMgjkTq50D4BxXwekJwm9xvGLZjDB7igLIoJ3c3E tptilTkMUy/aaqpVoxiACWEoYblP/j2jYJOFoGyo= Received: from send105.i.mail.ru (send105.i.mail.ru [89.221.237.200]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 8A6A31BB8FFA for ; Fri, 13 Mar 2026 11:52:02 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 8A6A31BB8FFA Received: by exim-smtp-64cdfc6c8d-kfk4r with esmtpa (envelope-from ) id 1w0yFV-00000000ASo-1ft8; Fri, 13 Mar 2026 11:52:01 +0300 Content-Type: multipart/alternative; boundary="------------w9qSdX000mRU0AO10EznHdRd" Message-ID: <4454d7e1-43a6-4151-b39f-26fec9a7f04d@tarantool.org> Date: Fri, 13 Mar 2026 11:52:00 +0300 MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Content-Language: en-US To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org References: <334e8dd174e6f4c9a03b3a5827c36d5353a85238.1773330679.git.skaplun@tarantool.org> In-Reply-To: <334e8dd174e6f4c9a03b3a5827c36d5353a85238.1773330679.git.skaplun@tarantool.org> X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: EEAE043A70213CC8 X-77F55803: 4F1203BC0FB41BD9BD0DAF1C516324DA8A0CB8B4FFCDF8B94285B12B92102899182A05F5380850404C228DA9ACA6FE27CC6DE9DAD0EB64693DE06ABAFEAF6705984368EF0A95F5036EBE2C5179AA64B5E1BDF786614B4017 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE72C932D6B54DFB189EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637AC83A81C8FD4AD23D82A6BABE6F325AC2E85FA5F3EDFCBAA7353EFBB5533756627194F95AE6E7C2D4D257C5166AA8B4C6093F86DA9492547530044A065ACB6A7389733CBF5DBD5E913377AFFFEAFD269176DF2183F8FC7C0A29E2F051442AF778941B15DA834481FCF19DD082D7633A0EF3E4896CB9E6436389733CBF5DBD5E9D5E8D9A59859A8B636DA1BED736F9328CC7F00164DA146DA6F5DAA56C3B73B237318B6A418E8EAB86D1867E19FE14079C09775C1D3CA48CF3D321E7403792E342EB15956EA79C166A417C69337E82CC275ECD9A6C639B01B78DA827A17800CE778B471BB9634AD8A731C566533BA786AA5CC5B56E945C8DA X-C1DE0DAB: 0D63561A33F958A5214987B1A7C6B97C5002B1117B3ED696659D5935643D6922957033528158102E823CB91A9FED034534781492E4B8EEADADEF88395FA75C5FBDAD6C7F3747799A X-C8649E89: 1C3962B70DF3F0AD73CAD6646DEDE191716CD42B3DD1D34CAB70F9BE574AE9C625B6776AC983F447FC0B9F89525902EE6F57B2FD27647F25E66C117BDB76D659D0A613E36676D27822238C4DDDE38D03C15C4FBA622AABD6ADCE6B2A2C85B217F69C13DCD29BCAD1B8341EE9D5BE9A0A70D6571F40B331B7731DE5C5F8210CCBD3310DF1F630D8F68CD93680B12512CF4C41F94D744909CE2512F26BEC029E55448553D2254B8D95CD72808BE417F3B9E0E7457915DAA85F X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu53w8ahmwBjZKM/YPHZyZHvz5uv+WouB9+ObcCpyrx6l7KImUglyhkEat/+ysWwi0gdhEs0JGjl6ggRWTy1haxBpVdbIX1nthFXMZebaIdHP2ghjoIc/363UZI6Kf1ptIMVdbVVJCphTR/v05qLFAmjZg= X-DA7885C5: 8343B9BE20A4EA64F255D290C0D534F92C3018FC5C07CB4C26ADE2A772DA086CDA50D712DD2C5C365B1A4C17EAA7BC4BEF2421ABFA55128DAF83EF9164C44C7E X-Mailru-Sender: 689FA8AB762F7393520AF17B8A65FDE20521E394966EF41E993E9350BB827CFE5E170A3321DA9523EF86D5F70DA33880E41E8EF7A07863ECB274557F927329BE2DDF8182D28ACDB545BD1C3CC395C826B4A721A3011E896F X-Mras: Ok Subject: Re: [Tarantool-patches] [PATCH luajit 1/2] Prevent recording of loops with -0 step or NaN values. X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" This is a multi-part message in MIME format. --------------w9qSdX000mRU0AO10EznHdRd Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit Hi, Sergey, thanks for the patch! LGTM with minor comments. Sergey On 3/12/26 18:55, Sergey Kaplun wrote: > From: Mike Pall > > Thanks to Sergey Kaplun. > > (cherry picked from commit 54cce2e1719a15fc33e40c57dbc3d62e9c104b03) > > The -0 step and NaN control variable values may lead to the traces > with always failed guards. > > This patch forbids recording of such traces since these traces are not > very useful. Unfortunately, this breaks for loop recording in DUALNUM > mode. This will be fixed in the next commit. > > Sergey Kaplun: > * added the description and the test for the problem > > Part of tarantool/tarantool#12134 > --- > src/lj_record.c | 6 ++ > .../lj-1432-minus-zero-step.test.lua | 57 +++++++++++++ > .../lj-1433-nan-for-control-var.test.lua | 79 +++++++++++++++++++ > 3 files changed, 142 insertions(+) > create mode 100644 test/tarantool-tests/lj-1432-minus-zero-step.test.lua > create mode 100644 test/tarantool-tests/lj-1433-nan-for-control-var.test.lua > > diff --git a/src/lj_record.c b/src/lj_record.c > index 81da43f5..a3a68b57 100644 > --- a/src/lj_record.c > +++ b/src/lj_record.c > @@ -509,6 +509,12 @@ static LoopEvent rec_for(jit_State *J, const BCIns *fori, int isforl) > LoopEvent ev; > TRef stop; > IRType t; > + /* Avoid semantic mismatches and always failing guards. */ > + if (tvisnan(&tv[FORL_IDX]) || > + tvisnan(&tv[FORL_STOP]) || > + tvisnan(&tv[FORL_STEP]) || > + tvismzero(&tv[FORL_STEP])) > + lj_trace_err(J, LJ_TRERR_GFAIL); > if (isforl) { /* Handle FORL/JFORL opcodes. */ > TRef idx = tr[FORL_IDX]; > if (mref(J->scev.pc, const BCIns) == fori && tref_ref(idx) == J->scev.idx) { > diff --git a/test/tarantool-tests/lj-1432-minus-zero-step.test.lua b/test/tarantool-tests/lj-1432-minus-zero-step.test.lua > new file mode 100644 > index 00000000..112153dc > --- /dev/null > +++ b/test/tarantool-tests/lj-1432-minus-zero-step.test.lua > @@ -0,0 +1,57 @@ > +local tap = require('tap') > + > +-- Test file to check the correct recording of -0 step for value. > +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1432. > + > +local test = tap.test('lj-1432-minus-zero-step'):skipcond({ > + ['Test requires JIT enabled'] = not jit.status(), > +}) > + > +test:plan(2) > + > +local traceinfo = require('jit.util').traceinfo > + > +local function trace_slot() > + local counter = 0 > + local slot = -0 > + -- Run the inner trace several times. Before the patch, it leads > + -- to several child traces due to the always failed guards. > + while true do > + if counter > 5 then break end > + counter = counter + 1; > + -- luacheck: ignore > + for _ = 1, 1, slot do > + break > + end > + end > +end > + > +local function trace_const() > + local counter = 0 > + -- Run the inner trace several times. Before the patch, it leads > + -- to several child traces due to the always failed guards. > + while true do > + if counter > 5 then break end > + counter = counter + 1; > + -- luacheck: ignore > + for _ = 1, 1, -0 do > + break > + end > + end > +end > + > +local function test_trace_recorded(test_payload) > + jit.flush() > + -- Reset hotcounters. nit: comment can be omitted > + jit.opt.start('hotloop=1', 'hotexit=1') > + test_payload() > + return traceinfo(1) > +end > + > +-- The -0 step leads to the always failed guard, so such traces > +-- are now aborted and not recorded. > + > +test:ok(not test_trace_recorded(trace_slot), 'no trace recorded -0 as slot') > +test:ok(not test_trace_recorded(trace_const), 'no trace recorded -0 as const') > + > +test:done(true) > diff --git a/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua b/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua > new file mode 100644 > index 00000000..1f67f0ad > --- /dev/null > +++ b/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua > @@ -0,0 +1,79 @@ > +local tap = require('tap') > + > +-- Test file to check the correct recording of for control > +-- variable with NaN value. > +-- See alsohttps://github.com/LuaJIT/LuaJIT/issues/1433. > + > +local test = tap.test('lj-1433-nan-for-control-var'):skipcond({ I would rename: s/lj-1433-nan-for-control-var/lj-1433-nan-for-loop-control-var/ Feel free to ignore. > + ['Test requires JIT enabled'] = not jit.status(), > +}) > + > +test:plan(3) > + > +local traceinfo = require('jit.util').traceinfo > + > +local function trace_nan_start() nit: s/trace_nan_start/trace_nan_loop_start/ the same below > + local counter = 0 > + -- XXX: Use NaN as stack slot, not upvalue. > + local nan = 0 / 0 > + -- Run the inner trace several times. Before the patch, it leads > + -- to the trace with always fail guard. > + while true do > + if counter > 5 then break end > + counter = counter + 1; > + -- luacheck: ignore > + for _ = nan, 1, 1 do > + break > + end > + end > +end > + > +local function trace_nan_stop() > + local counter = 0 > + -- XXX: Use NaN as stack slot, not upvalue. > + local nan = 0 / 0 > + -- Run the inner trace several times. Before the patch, it leads > + -- to the trace with always fail guard. > + while true do > + if counter > 5 then break end > + counter = counter + 1; > + -- luacheck: ignore > + for _ = 1, nan, 1 do > + break > + end > + end > +end > + > +local function trace_nan_step() > + local counter = 0 > + -- XXX: Use NaN as stack slot, not upvalue. > + local nan = 0 / 0 > + -- Run the inner trace several times. Before the patch, it leads > + -- to several child traces due to the always failed guards. > + while true do > + if counter > 5 then break end > + counter = counter + 1; > + -- luacheck: ignore > + for _ = 1, 1, nan do > + break > + end > + end > +end > + > +local function test_trace_recorded(test_payload) > + jit.flush() > + -- Reset hotcounters. > + jit.opt.start('hotloop=1', 'hotexit=1') > + test_payload() > + return traceinfo(1) > +end > + > +-- The NaN control vars leads to the always failed guard, so such s/control/loop control/ > +-- traces are now aborted and not recorded. > + > +test:ok(not test_trace_recorded(trace_nan_start), 'no trace recorded NaN start') > +test:ok(not test_trace_recorded(trace_nan_stop), 'no trace recorded NaN stop') > +test:ok(not test_trace_recorded(trace_nan_step), 'no trace recorded NaN step') > + > +test:done(true) > + --------------w9qSdX000mRU0AO10EznHdRd Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 7bit

Hi, Sergey,

thanks for the patch! LGTM with minor comments.

Sergey

On 3/12/26 18:55, Sergey Kaplun wrote:
From: Mike Pall <mike>

Thanks to Sergey Kaplun.

(cherry picked from commit 54cce2e1719a15fc33e40c57dbc3d62e9c104b03)

The -0 step and NaN control variable values may lead to the traces
with always failed guards.

This patch forbids recording of such traces since these traces are not
very useful. Unfortunately, this breaks for loop recording in DUALNUM
mode. This will be fixed in the next commit.

Sergey Kaplun:
* added the description and the test for the problem

Part of tarantool/tarantool#12134
---
 src/lj_record.c                               |  6 ++
 .../lj-1432-minus-zero-step.test.lua          | 57 +++++++++++++
 .../lj-1433-nan-for-control-var.test.lua      | 79 +++++++++++++++++++
 3 files changed, 142 insertions(+)
 create mode 100644 test/tarantool-tests/lj-1432-minus-zero-step.test.lua
 create mode 100644 test/tarantool-tests/lj-1433-nan-for-control-var.test.lua

diff --git a/src/lj_record.c b/src/lj_record.c
index 81da43f5..a3a68b57 100644
--- a/src/lj_record.c
+++ b/src/lj_record.c
@@ -509,6 +509,12 @@ static LoopEvent rec_for(jit_State *J, const BCIns *fori, int isforl)
   LoopEvent ev;
   TRef stop;
   IRType t;
+  /* Avoid semantic mismatches and always failing guards. */
+  if (tvisnan(&tv[FORL_IDX]) ||
+      tvisnan(&tv[FORL_STOP]) ||
+      tvisnan(&tv[FORL_STEP]) ||
+      tvismzero(&tv[FORL_STEP]))
+    lj_trace_err(J, LJ_TRERR_GFAIL);
   if (isforl) {  /* Handle FORL/JFORL opcodes. */
     TRef idx = tr[FORL_IDX];
     if (mref(J->scev.pc, const BCIns) == fori && tref_ref(idx) == J->scev.idx) {
diff --git a/test/tarantool-tests/lj-1432-minus-zero-step.test.lua b/test/tarantool-tests/lj-1432-minus-zero-step.test.lua
new file mode 100644
index 00000000..112153dc
--- /dev/null
+++ b/test/tarantool-tests/lj-1432-minus-zero-step.test.lua
@@ -0,0 +1,57 @@
+local tap = require('tap')
+
+-- Test file to check the correct recording of -0 step for value.
+-- See also https://github.com/LuaJIT/LuaJIT/issues/1432.
+
+local test = tap.test('lj-1432-minus-zero-step'):skipcond({
+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(2)
+
+local traceinfo = require('jit.util').traceinfo
+
+local function trace_slot()
+  local counter = 0
+  local slot = -0
+  -- Run the inner trace several times. Before the patch, it leads
+  -- to several child traces due to the always failed guards.
+  while true do
+    if counter > 5 then break end
+    counter = counter + 1;
+    -- luacheck: ignore
+    for _ = 1, 1, slot do
+      break
+    end
+  end
+end
+
+local function trace_const()
+  local counter = 0
+  -- Run the inner trace several times. Before the patch, it leads
+  -- to several child traces due to the always failed guards.
+  while true do
+    if counter > 5 then break end
+    counter = counter + 1;
+    -- luacheck: ignore
+    for _ = 1, 1, -0 do
+      break
+    end
+  end
+end
+
+local function test_trace_recorded(test_payload)
+  jit.flush()
+  -- Reset hotcounters.
nit: comment can be omitted
+  jit.opt.start('hotloop=1', 'hotexit=1')
+  test_payload()
+  return traceinfo(1)
+end
+
+-- The -0 step leads to the always failed guard, so such traces
+-- are now aborted and not recorded.
+
+test:ok(not test_trace_recorded(trace_slot), 'no trace recorded -0 as slot')
+test:ok(not test_trace_recorded(trace_const), 'no trace recorded -0 as const')
+
+test:done(true)
diff --git a/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua b/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua
new file mode 100644
index 00000000..1f67f0ad
--- /dev/null
+++ b/test/tarantool-tests/lj-1433-nan-for-control-var.test.lua
@@ -0,0 +1,79 @@
+local tap = require('tap')
+
+-- Test file to check the correct recording of for control
+-- variable with NaN value.
+-- See also https://github.com/LuaJIT/LuaJIT/issues/1433.
+
+local test = tap.test('lj-1433-nan-for-control-var'):skipcond({

I would rename: s/lj-1433-nan-for-control-var/lj-1433-nan-for-loop-control-var/

Feel free to ignore.

+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(3)
+
+local traceinfo = require('jit.util').traceinfo
+
+local function trace_nan_start()

nit: s/trace_nan_start/trace_nan_loop_start/

the same below
+  local counter = 0
+  -- XXX: Use NaN as stack slot, not upvalue.
+  local nan = 0 / 0
+  -- Run the inner trace several times. Before the patch, it leads
+  -- to the trace with always fail guard.
+  while true do
+    if counter > 5 then break end
+    counter = counter + 1;
+    -- luacheck: ignore
+    for _ = nan, 1, 1 do
+      break
+    end
+  end
+end
+
+local function trace_nan_stop()
+  local counter = 0
+  -- XXX: Use NaN as stack slot, not upvalue.
+  local nan = 0 / 0
+  -- Run the inner trace several times. Before the patch, it leads
+  -- to the trace with always fail guard.
+  while true do
+    if counter > 5 then break end
+    counter = counter + 1;
+    -- luacheck: ignore
+    for _ = 1, nan, 1 do
+      break
+    end
+  end
+end
+
+local function trace_nan_step()
+  local counter = 0
+  -- XXX: Use NaN as stack slot, not upvalue.
+  local nan = 0 / 0
+  -- Run the inner trace several times. Before the patch, it leads
+  -- to several child traces due to the always failed guards.
+  while true do
+    if counter > 5 then break end
+    counter = counter + 1;
+    -- luacheck: ignore
+    for _ = 1, 1, nan do
+      break
+    end
+  end
+end
+
+local function test_trace_recorded(test_payload)
+  jit.flush()
+  -- Reset hotcounters.
+  jit.opt.start('hotloop=1', 'hotexit=1')
+  test_payload()
+  return traceinfo(1)
+end
+
+-- The NaN control vars leads to the always failed guard, so such
s/control/loop control/
+-- traces are now aborted and not recorded.
+
+test:ok(not test_trace_recorded(trace_nan_start), 'no trace recorded NaN start')
+test:ok(not test_trace_recorded(trace_nan_stop), 'no trace recorded NaN stop')
+test:ok(not test_trace_recorded(trace_nan_step), 'no trace recorded NaN step')
+
+test:done(true)
+
--------------w9qSdX000mRU0AO10EznHdRd--