Skip to content

Commit af5d38f

Browse files
Mike PallBuristan
authored andcommitted
Invalidate SCEV entry when returning to lower frame.
Thanks to Zhongwei Yao. (cherry picked from commit 65c8493) When returning to the lower frame, LuaJIT does not clear the Scalar Evolution analysis entry. Hence, this may lead to its invalid usage in the next function called if the IR references match. The further analysis is invalid and may lead to the assertion failure. This patch invalidates the ScEv entry IR reference index when returning to the lower frame. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#11691 Reviewed-by: Sergey Bronnikov <[email protected]> Signed-off-by: Sergey Kaplun <[email protected]>
1 parent 249ea3c commit af5d38f

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

src/lj_record.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -898,6 +898,7 @@ void lj_record_ret(jit_State *J, BCReg rbase, ptrdiff_t gotresults)
898898
emitir(IRTG(IR_RETF, IRT_PGC), trpt, trpc);
899899
J->retdepth++;
900900
J->needsnap = 1;
901+
J->scev.idx = REF_NIL;
901902
lj_assertJ(J->baseslot == 1+LJ_FR2, "bad baseslot for return");
902903
/* Shift result slots up and clear the slots of the new frame below. */
903904
memmove(J->base + cbase, J->base-1-LJ_FR2, sizeof(TRef)*nresults);
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
local tap = require('tap')
2+
3+
-- Test file to demonstrate LuaJIT's incorrect Scalar Evolution
4+
-- analysis for recording of return to a lower frame.
5+
-- See also: https://github.com/LuaJIT/LuaJIT/pull/1115.
6+
local test = tap.test('lj-1115-invalid-scev-entry-lower-frame'):skipcond({
7+
['Test requires JIT enabled'] = not jit.status(),
8+
})
9+
10+
test:plan(1)
11+
12+
local HOTLOOP = 1
13+
local HOTEXIT = 1
14+
local RECORD_IDX = HOTLOOP + 1
15+
-- Number of iterations to start recording side trace with two
16+
-- iterations in the cycle.
17+
local NITER = RECORD_IDX + HOTEXIT + 2
18+
19+
local function test_function(tab)
20+
-- XXX: For reproducing the issue, it is necessary to avoid
21+
-- UGET. Local functions use MOV and take the same IR slots.
22+
local function trace_root(data)
23+
-- Start of the trace, setup ScEv entry.
24+
for i = 1, #data - 1 do
25+
-- Start of the side trace by the hmask check.
26+
if data[i].t == 'a' then
27+
return i + 1
28+
end
29+
end
30+
-- Unreachable in this test.
31+
return nil
32+
end
33+
34+
local function other_scev(data, start)
35+
for i = start, #data - 1 do
36+
-- The ScEv entry matches the recorded IR from the parent
37+
-- trace before the patch. It leads to the assertion
38+
-- failure.
39+
if data[i].t == 'a' then
40+
return
41+
end
42+
end
43+
end
44+
45+
-- Record the root trace first. Then record the side trace
46+
-- returning to the lower frame (this function).
47+
local start = trace_root(tab)
48+
-- The ScEv entry is invalid after the return to the lower
49+
-- frame. Record the trace with another range in the ScEv entry
50+
-- to obtain the error.
51+
return start, other_scev(tab, start)
52+
end
53+
54+
local data = {}
55+
for i = 1, NITER do
56+
data[#data + 1] = {t = 'a' .. i}
57+
end
58+
59+
-- Change the hmask value to start the side trace recording.
60+
data[RECORD_IDX] = {}
61+
-- Setup for the trace's return to the lower frame.
62+
data[NITER - 2] = {t = 'a'}
63+
64+
jit.opt.start('hotloop=' .. HOTLOOP, 'hotexit=' .. HOTEXIT)
65+
66+
test_function(data)
67+
68+
test:ok(true, 'correct ScEv entry invalidation for return to a lower frame')
69+
test:done(true)

0 commit comments

Comments
 (0)