Skip to content

Commit f3eff48

Browse files
Mike Palligormunkin
Mike Pall
authored andcommitted
Fix ABC FOLD rule with constants.
Reported by XmiliaH. (cherry-picked from commit c8bcf1e) `fold_abc_k()` doesn't patch the first ABC check when the right constant operand is negative. This leads to out-of-bounds access from the array on a trace. This patch casts the operands to uint32_t for comparison. If the right IR contains a negative integer, the second IR will always be patched. Also, because the ABC check on the trace is unordered, this guard will always fail. Also, this fold rule creates new instructions that reference operands across PHIs. This opens the room for other optimizations (like DCE), so some guards become eliminated, and we use out-of-bounds access from the array part of the table on trace. This patch adds the missing `PHIBARRIER()` check. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#9145 Reviewed-by: Maxim Kokryashkin <[email protected]> Reviewed-by: Sergey Bronnikov <[email protected]> Signed-off-by: Igor Munkin <[email protected]> (cherry picked from commit 4018d3a)
1 parent 1f4ceab commit f3eff48

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

src/lj_opt_fold.c

+3-2
Original file line numberDiff line numberDiff line change
@@ -1877,14 +1877,15 @@ LJFOLDF(abc_fwd)
18771877
LJFOLD(ABC any KINT)
18781878
LJFOLDF(abc_k)
18791879
{
1880+
PHIBARRIER(fleft);
18801881
if (LJ_LIKELY(J->flags & JIT_F_OPT_ABC)) {
18811882
IRRef ref = J->chain[IR_ABC];
18821883
IRRef asize = fins->op1;
18831884
while (ref > asize) {
18841885
IRIns *ir = IR(ref);
18851886
if (ir->op1 == asize && irref_isk(ir->op2)) {
1886-
int32_t k = IR(ir->op2)->i;
1887-
if (fright->i > k)
1887+
uint32_t k = (uint32_t)IR(ir->op2)->i;
1888+
if ((uint32_t)fright->i > k)
18881889
ir->op2 = fins->op2;
18891890
return DROPFOLD;
18901891
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
local tap = require('tap')
2+
3+
-- Test file to demonstrate LuaJIT's incorrect fold optimization
4+
-- for Array Bound Check for constants.
5+
-- ABC(asize, k1), ABC(asize k2) ==> ABC(asize, max(k1, k2)).
6+
-- See also https://github.com/LuaJIT/LuaJIT/issues/794.
7+
8+
local test = tap.test('lj-794-abc-fold-constants'):skipcond({
9+
['Test requires JIT enabled'] = not jit.status(),
10+
})
11+
12+
test:plan(2)
13+
14+
local MAGIC_UNUSED = 42
15+
16+
local function abc_check_sign()
17+
local tab = {MAGIC_UNUSED}
18+
local return_value = 0
19+
local abc_limit = 1
20+
-- No need to run the loop on the first call. We will take
21+
-- the side exit anyway.
22+
for i = 1, 3 do
23+
-- Add an additional ABC check to be merged with.
24+
if i > 1 then
25+
-- luacheck: ignore
26+
return_value = tab[1]
27+
return_value = tab[abc_limit]
28+
-- XXX: Just use some negative number.
29+
abc_limit = -1000000
30+
end
31+
end
32+
return return_value
33+
end
34+
35+
jit.opt.start('hotloop=1')
36+
37+
-- Compile the loop.
38+
abc_check_sign()
39+
-- Run the loop.
40+
test:is(abc_check_sign(), nil, 'correct ABC constant rule for negative values')
41+
42+
-- Now test the second issue, when ABC optimization applies for
43+
-- operands across PHIs.
44+
45+
-- XXX: Reset hotcounters to avoid collisions.
46+
jit.opt.start('hotloop=1')
47+
48+
local tab_array = {}
49+
local small_tab = {MAGIC_UNUSED}
50+
local full_tab = {}
51+
52+
-- First, create tables with different asizes, to be used in PHI.
53+
-- Create a large enough array part for the noticeable
54+
-- out-of-bounds access.
55+
for i = 1, 8 do
56+
full_tab[i] = MAGIC_UNUSED
57+
end
58+
59+
-- Now, store these tables in the array. The PHI should be used in
60+
-- the trace to distinguish asizes from the variant and the
61+
-- invariant parts of the loop for the future ABC check.
62+
-- Nevertheless, before the patch, the ABC IR and the
63+
-- corresponding PHI are folded via optimization. This leads to
64+
-- incorrect behaviour.
65+
-- We need 5 iterations to execute both the variant and the
66+
-- invariant parts of the trace below.
67+
for i = 1, 5 do
68+
-- On the 3rd iteration, the recording is started.
69+
if i > 3 then
70+
tab_array[i] = small_tab
71+
else
72+
tab_array[i] = full_tab
73+
end
74+
end
75+
76+
local result
77+
local alias_tab = tab_array[1]
78+
-- Compile a trace.
79+
-- Run 5 iterations to execute both the variant and the invariant
80+
-- parts.
81+
for i = 1, 5 do
82+
local previous_tab = alias_tab
83+
alias_tab = tab_array[i]
84+
-- Additional ABC check to fold.
85+
-- luacheck: ignore
86+
result = alias_tab[1]
87+
result = previous_tab[8]
88+
end
89+
90+
test:is(result, nil, 'correct ABC constant rule across PHI')
91+
92+
test:done(true)

0 commit comments

Comments
 (0)