Skip to content

Commit 66c6d16

Browse files
Mike Palligormunkin
authored andcommitted
Fix bytecode register allocation for comparisons.
(cherry picked from commit 2f3f078) When LuaJIT is built with LJ_FR2 (e.g. with GC64 mode enabled), information about frame takes two slots -- the first takes the TValue with the function to be called, the second takes the framelink. The JIT recording machinery does pretty the same -- the function IR_KGC is loaded in the first slot, and the second is set to TREF_FRAME value. This value should be rewritten after return from a callee. This slot is cleared either by return values or manually (set to zero), when there are no values to return. The latter case is done by the next bytecode with RA dst mode. This obliges that the destination of RA takes the next slot after TREF_FRAME. Hence, an earlier instruction must use the smallest possible destination register (see `lj_record_ins()` for the details). Bytecode emitter swaps operands for ISGT and ISGE comparisons. As a result, the aforementioned rule for registers allocations may be violated. When it happens for a chunk being recorded, the slot with TREF_FRAME is not rewritten (but the next empty slot after TREF_FRAME is). This leads to JIT slots inconsistency and assertion failure in `rec_check_slots()` during recording of the next bytecode instruction. This patch fixes bytecode register allocation by changing the VM register allocation order in case of ISGT and ISGE bytecodes. Sergey Kaplun: * added the description and the test for the problem Resolves tarantool/tarantool#6227 Part of tarantool/tarantool#5629 Reviewed-by: Sergey Ostanevich <[email protected]> Reviewed-by: Igor Munkin <[email protected]> Signed-off-by: Igor Munkin <[email protected]>
1 parent 6469d70 commit 66c6d16

File tree

2 files changed

+59
-2
lines changed

2 files changed

+59
-2
lines changed

src/lj_parse.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -853,9 +853,12 @@ static void bcemit_comp(FuncState *fs, BinOpr opr, ExpDesc *e1, ExpDesc *e2)
853853
e1 = e2; e2 = eret; /* Swap operands. */
854854
op = ((op-BC_ISLT)^3)+BC_ISLT;
855855
expr_toval(fs, e1);
856+
ra = expr_toanyreg(fs, e1);
857+
rd = expr_toanyreg(fs, e2);
858+
} else {
859+
rd = expr_toanyreg(fs, e2);
860+
ra = expr_toanyreg(fs, e1);
856861
}
857-
rd = expr_toanyreg(fs, e2);
858-
ra = expr_toanyreg(fs, e1);
859862
ins = BCINS_AD(op, ra, rd);
860863
}
861864
/* Using expr_free might cause asserts if the order is wrong. */
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
local tap = require('tap')
2+
local test = tap.test('gh-6227-bytecode-allocator-for-comparisons')
3+
test:plan(1)
4+
5+
-- Test file to demonstrate assertion failure during recording
6+
-- wrong allocated bytecode for comparisons.
7+
-- See also https://github.com/tarantool/tarantool/issues/6227.
8+
9+
-- Need function with RET0 bytecode to avoid reset of
10+
-- the first JIT slot with frame info. Also need no assignments
11+
-- by the caller.
12+
local function empty() end
13+
14+
local uv = 0
15+
16+
-- This function needs to reset register enumerating.
17+
-- `J->maxslot` is initialized with `nargs` (i.e. zero in this
18+
-- case) in `rec_call_setup()`.
19+
local function bump_frame()
20+
-- First call function with RET0 to set TREF_FRAME in the
21+
-- last slot.
22+
empty()
23+
-- The old bytecode to be recorded looks like the following:
24+
-- 0000 . FUNCF 4
25+
-- 0001 . UGET 0 0 ; empty
26+
-- 0002 . CALL 0 1 1
27+
-- 0000 . . JFUNCF 1 1
28+
-- 0001 . . RET0 0 1
29+
-- 0002 . CALL 0 1 1
30+
-- 0003 . UGET 0 0 ; empty
31+
-- 0004 . UGET 3 1 ; uv
32+
-- 0005 . KSHORT 2 1
33+
-- 0006 . ISLT 3 2
34+
-- Test ISGE or ISGT bytecode. These bytecodes swap their
35+
-- operands (consider ISLT above).
36+
-- Two calls of `empty()` function in a row is necessary for 2
37+
-- slot gap in LJ_FR2 mode.
38+
-- Upvalue loads before KSHORT, so the difference between slot
39+
-- for upvalue `empty` (function to be called) and slot for
40+
-- upvalue `uv` is more than 2. Hence, TREF_FRAME slot is not
41+
-- rewritten by the bytecode after return from `empty()`
42+
-- function as expected. That leads to recording slots
43+
-- inconsistency and assertion failure at `rec_check_slots()`.
44+
empty(1>uv)
45+
end
46+
47+
jit.opt.start('hotloop=1')
48+
49+
for _ = 1, 3 do
50+
bump_frame()
51+
end
52+
53+
test:ok(true)
54+
os.exit(test:check() and 0 or 1)

0 commit comments

Comments
 (0)