Skip to content

Commit

Permalink
Fix string.char() recording with no arguments.
Browse files Browse the repository at this point in the history
(cherry picked from commit dfa692b)

`string.char()` call without arguments yields an empty string. JIT
recording machinery doesn’t handle this case. Each recording of a fast
function expects 1 result by default. Hence, when return from this call
is recorded some garbarge value is considered as a result to yield. It
is loaded into the corresponding slot as an IR with `IRT_NUM` type. It
leads to assertion failure in `rec_check_slots()`, when a next bytecode
is recorded, because type of TValue on the stack (`LJ_STR`) isn't the
same as IR (and TRef) type.

This patch handles the case without arguments by the loading of IR with
empty string reference into the corresponding slot. It reuses assumption
of one result by default, hence there is no case for `i == 1` in the
code.

Sergey Kaplun:
* added the description and the test for the problem

Resolves tarantool/tarantool#6371
  • Loading branch information
Mike Pall authored and Buristan committed Jan 28, 2022
1 parent 8304ca9 commit 80c84ca
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/lj_ffrecord.c
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,8 @@ static void LJ_FASTCALL recff_string_char(jit_State *J, RecordFFData *rd)
for (i = 0; J->base[i] != 0; i++)
tr = emitir(IRT(IR_BUFPUT, IRT_PGC), tr, J->base[i]);
J->base[0] = emitir(IRT(IR_BUFSTR, IRT_STR), tr, hdr);
} else if (i == 0) {
J->base[0] = lj_ir_kstr(J, &J2G(J)->strempty);
}
UNUSED(rd);
}
Expand Down
29 changes: 29 additions & 0 deletions test/tarantool-tests/gh-6371-string-char-no-arg.test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
local tap = require('tap')

-- Test file to demonstrate assertion after `string.char()`
-- recording.
-- See also, https://github.com/tarantool/tarantool/issues/6371.

local test = tap.test('gh-6371-string-char-no-arg')
-- XXX: Number of loop iterations.
-- * 1 -- instruction becomes hot.
-- * 2 -- recording of the loop body.
-- * 3 -- required for trace finalization, but this iteration runs the
-- generated mcode. NB: the issue doesn't occur, since execution leaves
-- the trace on table resizing guard.
-- * 4 -- reproduces the issue.
local NTEST = 4
test:plan(NTEST)

-- Storage for the results to avoid trace aborting by `test:ok()`.
local results = {}
jit.opt.start('hotloop=1')
for _ = 1, NTEST do
table.insert(results, string.char())
end

for i = 1, NTEST do
test:ok(results[i] == '', 'correct recording of string.char() without args')
end

os.exit(test:check() and 0 or 1)

0 comments on commit 80c84ca

Please sign in to comment.