Skip to content

Commit 553d906

Browse files
Mike Palligormunkin
Mike Pall
authored andcommitted
Prevent CSE of a REF_BASE operand across IR_RETF.
Reported by XmiliaH. (cherry-picked from commit e73916d) The RETF IR has a side effect: it shifts base when returning to a lower frame, i.e., it affects `REF_BASE` IR (0000) (thus, we can say that this IR is violating SSA form). So any optimization of IRs with `REF_BASE` as an operand across RETF IR may lead to incorrect optimizations (see details in the test file). This patch adds rules to the folding engine to prevent CSE across `IR_RETF` for all possible IRs containing REF_BASE. Sergey Kaplun: * added the description and the test for the problem Part of tarantool/tarantool#9145 Reviewed-by: Sergey Bronnikov <[email protected]> Reviewed-by: Maxim Kokryashkin <[email protected]> Signed-off-by: Igor Munkin <[email protected]> (cherry picked from commit 804f85a)
1 parent 4809378 commit 553d906

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed

src/lj_opt_fold.c

+11
Original file line numberDiff line numberDiff line change
@@ -2313,6 +2313,17 @@ LJFOLDF(xload_kptr)
23132313
LJFOLD(XLOAD any any)
23142314
LJFOLDX(lj_opt_fwd_xload)
23152315

2316+
/* -- Frame handling ------------------------------------------------------ */
2317+
2318+
/* Prevent CSE of a REF_BASE operand across IR_RETF. */
2319+
LJFOLD(SUB any BASE)
2320+
LJFOLD(SUB BASE any)
2321+
LJFOLD(EQ any BASE)
2322+
LJFOLDF(fold_base)
2323+
{
2324+
return lj_opt_cselim(J, J->chain[IR_RETF]);
2325+
}
2326+
23162327
/* -- Write barriers ------------------------------------------------------ */
23172328

23182329
/* Write barriers are amenable to CSE, but not across any incremental
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
local tap = require('tap')
2+
3+
-- Test file to demonstrate incorrect FOLD optimization for IR
4+
-- with REF_BASE operand across IR RETF.
5+
-- See also, https://github.com/LuaJIT/LuaJIT/issues/784.
6+
7+
local test = tap.test('lj-784-cse-ref-base-over-retf'):skipcond({
8+
['Test requires JIT enabled'] = not jit.status(),
9+
})
10+
11+
test:plan(1)
12+
13+
-- The RETF IR has a side effect: it shifts base when returning to
14+
-- a lower frame, i.e., it affects `REF_BASE` IR (0000) (thus, we
15+
-- can say that this IR is violating SSA form).
16+
-- So any optimization of IRs with `REF_BASE` as an operand across
17+
-- RETF IR may lead to incorrect optimizations.
18+
-- In this test, SUB uref REF_BASE IR was eliminated, so instead
19+
-- the following trace:
20+
--
21+
-- 0004 p32 SUB 0003 0000
22+
-- 0005 > p32 UGT 0004 +32
23+
-- ...
24+
-- 0009 > p32 RETF proto: 0x407dc118 [0x407dc194]
25+
-- ...
26+
-- 0012 p32 SUB 0003 0000
27+
-- 0013 > p32 UGT 0012 +72
28+
--
29+
-- We got the following:
30+
--
31+
-- 0004 p32 SUB 0003 0000
32+
-- 0005 > p32 UGT 0004 +32
33+
-- ...
34+
-- 0009 > p32 RETF proto: 0x41ffe0c0 [0x41ffe13c]
35+
-- ...
36+
-- 0012 > p32 UGT 0004 +72
37+
--
38+
-- As you can see, the 0012 SUB IR is eliminated because it is the
39+
-- same as the 0004 IR. This leads to incorrect assertion guards
40+
-- in the resulted IR 0012 below.
41+
42+
local MAGIC = 42
43+
-- XXX: simplify `jit.dump()` output.
44+
local fmod = math.fmod
45+
46+
local function exit_with_retf(closure)
47+
-- Forcify stitch. Any NYI is OK here.
48+
fmod(1, 1)
49+
-- Call the closure so that we have emitted `uref - REF_BASE`.
50+
closure(0)
51+
-- Exit with `IR_RETF`. This will change `REF_BASE`.
52+
end
53+
54+
local function sub_uref_base(closure)
55+
local open_upvalue
56+
if closure == nil then
57+
closure = function(val)
58+
local old = open_upvalue
59+
open_upvalue = val
60+
return old
61+
end
62+
-- First, create an additional frame, so we got the trace,
63+
-- where the open upvalue reference is always < `REF_BASE`.
64+
sub_uref_base(closure)
65+
end
66+
for _ = 1, 4 do
67+
-- `closure` function is inherited from the previous frame.
68+
exit_with_retf(closure)
69+
open_upvalue = MAGIC
70+
-- The open upvalue guard will use CSE over `IR_RETF` for
71+
-- `uref - REF_BASE`. `IR_RETF` changed the value of
72+
-- `REF_BASE`.
73+
-- Thus, the guards afterwards take the wrong IR as the first
74+
-- operand, so they are not failed, and the wrong value is
75+
-- returned from the trace.
76+
open_upvalue = closure(0)
77+
end
78+
return open_upvalue
79+
end
80+
81+
jit.opt.start('hotloop=1')
82+
83+
local res = sub_uref_base()
84+
test:is(res, MAGIC, 'no SUB uref REF_BASE CSE across RETF')
85+
86+
test:done(true)

0 commit comments

Comments
 (0)