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 the framelink slot (the top slot 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 Sep 1, 2021
1 parent 814625f commit 1d3eec8
Show file tree
Hide file tree
Showing 2 changed files with 30 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
28 changes: 28 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,28 @@
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, 2 -- instruction becomes hot
-- 3 -- trace is recorded (considering loop recording specifics),
-- but bytecodes are still executed via VM
-- 4 -- trace is executed, need to check that emitted mcode is
-- correct
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 1d3eec8

Please sign in to comment.