From 1d3eec868d6a1d4665593b0d3a9e9543d98cc9fc Mon Sep 17 00:00:00 2001 From: Mike Pall Date: Tue, 14 Jan 2020 11:08:42 +0100 Subject: [PATCH] Fix string.char() recording with no arguments. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit dfa692b746c9de067857d5fc992a41730be3d99a) `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 --- src/lj_ffrecord.c | 2 ++ .../gh-6371-string-char-no-arg.test.lua | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 test/tarantool-tests/gh-6371-string-char-no-arg.test.lua diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c index 8dfa80edff..be890a93ee 100644 --- a/src/lj_ffrecord.c +++ b/src/lj_ffrecord.c @@ -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); } diff --git a/test/tarantool-tests/gh-6371-string-char-no-arg.test.lua b/test/tarantool-tests/gh-6371-string-char-no-arg.test.lua new file mode 100644 index 0000000000..6df93f07b5 --- /dev/null +++ b/test/tarantool-tests/gh-6371-string-char-no-arg.test.lua @@ -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)