Skip to content

Commit

Permalink
Fix FOLD rule for BUFHDR append.
Browse files Browse the repository at this point in the history
Reported by XmiliaH.

(cherry-picked from commit bc1bdbf)

`bufput_append()` may fold `BUFHDR RESET` + `BUFPUT` IRs to `BUFHDR
APPEND` even if the right operand (`BUFSTR`) is the PHI. If it's not the
last IR in the `BUFSTR` chain, this may lead to an incorrect resulting
value in the buffer, which contains a longer string since `APPEND` is
used instead of `RESET`.

This patch adds the corresponding check inside the fold rule.

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 e895818)
  • Loading branch information
Mike Pall authored and igormunkin committed Dec 6, 2023
1 parent 553d906 commit e07c2fd
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/lj_opt_fold.c
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@ LJFOLDF(bufput_append)
if ((J->flags & JIT_F_OPT_FWD) &&
!(fleft->op2 & IRBUFHDR_APPEND) &&
fleft->prev == fright->op2 &&
fleft->op1 == IR(fright->op2)->op1) {
fleft->op1 == IR(fright->op2)->op1 &&
!(irt_isphi(fright->t) && IR(fright->op2)->prev)) {
IRRef ref = fins->op1;
IR(ref)->op2 = (fleft->op2 | IRBUFHDR_APPEND); /* Modify BUFHDR. */
IR(ref)->op1 = fright->op1;
Expand Down
54 changes: 54 additions & 0 deletions test/tarantool-tests/lj-791-fold-bufhdr-append.test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
local tap = require('tap')

-- Test file to demonstrate the incorrect LuaJIT's optimization
-- `bufput_append()` for BUFPUT IR.
-- See also https://github.com/LuaJIT/LuaJIT/issues/791.

local test = tap.test('lj-791-fold-bufhdr-append'):skipcond({
['Test requires JIT enabled'] = not jit.status(),
})

test:plan(1)

local EMPTY_STR = ''
local prefix = 'Lu'
local result

jit.opt.start('hotloop=1')

-- The interesting part of IRs is the following (non-GC64 mode):
-- 0006 str BUFSTR 0005 0003
-- 0007 > str SLOAD #2 T
-- 0008 p32 BUFHDR [0x400004a0] RESET
-- 0009 p32 BUFPUT 0008 "Lu"
-- 0010 p32 BUFPUT 0009 0007
-- 0011 + str BUFSTR 0010 0008
-- 0012 + int ADD 0001 +1
-- 0013 > int LE 0012 +5
-- 0014 > --- LOOP ------------
-- 0015 p32 BUFHDR [0x400004a0] RESET

-- The instruction to be folded is the following:
-- 0016 p32 BUFPUT 0015 0011
--
-- The 0011 operand is PHI, which is not the last IR in the BUFSTR
-- chain (`ir->prev = REF_BIAS + 0006`). Folding this IR leads to
-- this resulting IR:
-- p32 BUFHDR 0010 APPEND
-- Which appends to buffer instead of resetting, so the resulting
-- string contains one more symbol.

-- XXX: Use 5 iterations to run the variant part of the loop.
for _ = 1, 5 do
result = prefix .. 'a'
-- We need a non-constant string to be appended to prevent more
-- aggressive optimizations. Use an empty string for
-- convenience. Also, use a constant string in the first operand
-- in the concatenation operator for more readable `jit.dump`
-- output.
prefix = 'Lu' .. EMPTY_STR
end

test:is(result, 'Lua', 'BUFPUT APPEND optimization is not applied for PHIs')

test:done(true)

0 comments on commit e07c2fd

Please sign in to comment.