Hi, Sergey,

thanks for the patch! LGTM

Sergey

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

Thanks to Nicholas Davies.

(cherry picked from commit 1c3b5a4d722598ecbb9219480142eda682e87bb1)

The previous commit forbids recording for the NaN control variables
stack slots. These checks are false positives for integers in DUALNUM
mode. This prevents any for loop with integers as slots from being
recorded.

This patch fixes the checks.

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

Part of tarantool/tarantool#12134
---
 src/lj_record.c                               |  6 ++---
 .../lj-1438-jit-for-canary.test.lua           | 22 +++++++++++++++++++
 2 files changed, 25 insertions(+), 3 deletions(-)
 create mode 100644 test/tarantool-tests/lj-1438-jit-for-canary.test.lua

diff --git a/src/lj_record.c b/src/lj_record.c
index a3a68b57..dbfc7f47 100644
--- a/src/lj_record.c
+++ b/src/lj_record.c
@@ -510,9 +510,9 @@ static LoopEvent rec_for(jit_State *J, const BCIns *fori, int isforl)
   TRef stop;
   IRType t;
   /* Avoid semantic mismatches and always failing guards. */
-  if (tvisnan(&tv[FORL_IDX]) ||
-      tvisnan(&tv[FORL_STOP]) ||
-      tvisnan(&tv[FORL_STEP]) ||
+  if ((tvisnum(&tv[FORL_IDX]) && tvisnan(&tv[FORL_IDX])) ||
+      (tvisnum(&tv[FORL_STOP]) && tvisnan(&tv[FORL_STOP])) ||
+      (tvisnum(&tv[FORL_STEP]) && tvisnan(&tv[FORL_STEP])) ||
       tvismzero(&tv[FORL_STEP]))
     lj_trace_err(J, LJ_TRERR_GFAIL);
   if (isforl) {  /* Handle FORL/JFORL opcodes. */
diff --git a/test/tarantool-tests/lj-1438-jit-for-canary.test.lua b/test/tarantool-tests/lj-1438-jit-for-canary.test.lua
new file mode 100644
index 00000000..4b67df4c
--- /dev/null
+++ b/test/tarantool-tests/lj-1438-jit-for-canary.test.lua
@@ -0,0 +1,22 @@
+local tap = require('tap')
+
+-- The test file to check the correct recording of the for loop.
+-- Used as a canary test, since we have none.
+-- See also https://github.com/LuaJIT/LuaJIT/issues/1438.
+
+local test = tap.test('lj-1438-jit-for-canary'):skipcond({
+  ['Test requires JIT enabled'] = not jit.status(),
+})
+
+test:plan(1)
+
+local traceinfo = require('jit.util').traceinfo
+
+jit.flush()
+jit.opt.start('hotloop=1')
+
+for _ = 1, 4 do end
+
+test:ok(traceinfo(1), 'simple for loop is recorded')
+
+test:done(true)