From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id AACEC6EC40; Thu, 8 Oct 2026 14:45:56 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org AACEC6EC40 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1791459956; bh=clp2xbfZT7BJ8NpM1TXPcqOt3gfvoXuydJen+RjslfM=; h=To:Date:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=fMpm9Yf8pPFZ/CbJjNDRhHYdS6ptvRP0h8QxSDvMpYyPSzVqMyYxhcq2QMLOy1dlm gFNV3bs/v0kwJ4op+lqxeZHfpf4QbKP/ZFJd+uUNj/pEWZXmepEKpmKzImv2fR2Ceo gtpf0uTlDkSl+643JBXnLVPHE/6pp8b/y/ZXOQVI= Received: from mail-lj1-f174.google.com (mail-lj1-f174.google.com [209.85.208.174]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 84A776EC40 for ; Thu, 8 Oct 2026 14:45:55 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 84A776EC40 Received: by mail-lj1-f174.google.com with SMTP id 38308e7fff4ca-3a1e9f55e03so57108691fa.1 for ; Thu, 08 Oct 2026 04:45:55 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1791459955; x=1792064755; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=PZO5N9+VOmCLvhq47dD3+EiJJejDAiBllLj0lkoYeuM=; b=uMEPTayiepsaJPPG07ngELEzruRp9Qh0bCawesvaQ0v1zNfp9+E6MdiVxxows9TZ4o rEc/lHNp0g19rYemsrrNyMNd9dkGvcZjHlh3ReETjNbVPWgDNckcsBt4BnNX4XGM6jJH DYJMnrGAhFzzVF+TScvSTLzQjbpEIdgLJpQbdgU/wW5M5PWVgn4fCgKcwVg3Jc1HSqiV A/QkRnOPC6fPkaJXww2b4rhf5ZmXl0sumHPsRvhuiXsgX6tC5/5lulGAOHBNCMbSYx0Y fkSB151gca7nlywHIPFGlHJIESknPZMjCoiLaB5Ec3jofDMHpM84SilSscnRgaiidAA+ nYbw== X-Gm-Message-State: AFq9FYLd1yU8/p6lAqcHVI+xmcqLJ0jd49+tSTWNH1gSxJhV8wG+jkqM 3SWrkM/QVDDgdmIwapUU+0xIvx1PU/sZ24rvelhueEqiPuGHUgb5OiBFMDAH5A== X-Gm-Gg: AYBFou3197Z1yDHaEhi6o5ONEAH3YpKVxC1FXJwjGcRIlGmSNfc1hqWoogDQqC111GG PuLWlY+zPNqaMWdPxIch37TmQmMSCXapZ0knClirErM0mdeV3ksUjgpeyeY1OF9O+8aGwPlS0Y6 qIN5LZH0PI7QIxPumycYHmuObvaClkJFAVw4AbE4EULfNLVVe0cIPEhN+m+KGhOBGOB2XWb/Btf N68TvIelfJBfowpM8lboNXQbMQf9piINdVnOOJG1v9NbuYrinc8iXQjy+JHbYuo02jvTDprUIUB CPum2auGiigs4Oa0c0Q7ep1HqHOuMQXXvqmizh5bwSG8BoN1hQkM0TkDXQC1hWAzpYjZglif/e2 mKJK3E5YCAWUCRBhKXGetOXeQxDwGXZR5Zr4OpFHdKCtENkcgwlDcT5yNFCJSxzp+KYJB9sXUgL UejGzSScU+CAGHhf+9ihjtd4JCq5GQwou5Ywfznx8J1jv693Qyom2uRgVKAat01vrNJvx1o22qD yF8Og5Kr1gQ7cpQ1QcsQiLMNw== X-Received: by 2002:a2e:be1b:0:b0:3a9:bb20:2882 with SMTP id 38308e7fff4ca-3a9bb2f711dmr2105891fa.26.1791459954454; Thu, 08 Oct 2026 04:45:54 -0700 (PDT) Received: from localhost ([79.164.223.111]) by smtp.gmail.com with ESMTPSA id 38308e7fff4ca-3a9a1d581f9sm16089721fa.17.2026.10.08.04.45.51 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 08 Oct 2026 04:45:51 -0700 (PDT) To: tarantool-patches@dev.tarantool.org, Sergey Kaplun , e.temirgaleev@tarantool.org Date: Thu, 8 Oct 2026 11:44:35 +0000 Message-ID: X-Mailer: git-send-email 2.51.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit] Optionally return PC position in jit.util.tracesnap(). X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Bronnikov via Tarantool-patches Reply-To: Sergey Bronnikov Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Mike Pall Suggested by Sergey Bronnikov. (cherry picked from commit 8e6520a7aecd0517e792b359afbbfd7274791f5f) The optional `getpos` argument is needed for ljopt [1], a tool that verifies the correctness of LuaJIT optimizations by translating the recorded IR into SMT-LIB and checking the formulas with an SMT solver. Traces often lack sufficient information for accurate modeling, so ljopt extracts the address of the bytecode pointed to by the snapshot directly from LuaJIT during execution. The LuaJIT internal API includes the function `jit.util.tracesnap(tr, sn)`. In principle, this function could be extended to return the program counter (pc). This patch does exactly that: an optional third argument is added, and when it is truthy the second value returned by `jit.util.tracesnap()` is the PC position of the snapshot in the bytecode of the function where the trace starts. Also, the patch adds a test that records a root loop trace and checks that the returned position is the `FORL` jump target (the loop entry), that no position is returned without the optional argument or with a false value, and that every snapshot of the trace reports a valid bytecode offset. 1. https://github.com/ligurio/ljopt Sergey Bronnikov: * added the description and the test for the feature --- Branch: https://github.com/tarantool/luajit/tree/ligurio/gh-xxxx-tracesnap-getpos src/lib_jit.c | 9 +- .../lj-1472-tracesnap-getpos.test.lua | 89 +++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 test/tarantool-tests/lj-1472-tracesnap-getpos.test.lua diff --git a/src/lib_jit.c b/src/lib_jit.c index 9f870f68f..5a0d38611 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c @@ -351,11 +351,12 @@ LJLIB_CF(jit_util_tracek) return 0; } -/* local snap = jit.util.tracesnap(tr, sn) */ +/* local snap = jit.util.tracesnap(tr, sn[, getpos]) */ LJLIB_CF(jit_util_tracesnap) { GCtrace *T = jit_checktrace(L); SnapNo sn = (SnapNo)lj_lib_checkint(L, 2); + int getpos = (L->base+2 < L->top && tvistruecond(L->base+2)); if (T && sn < T->nsnap) { SnapShot *snap = &T->snap[sn]; SnapEntry *map = &T->snapmap[snap->mapofs]; @@ -368,6 +369,12 @@ LJLIB_CF(jit_util_tracesnap) for (n = 0; n < nent; n++) setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]); setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0)); + if (getpos) { + const BCIns *pc = snap_pc(&map[nent]), *startpc = pc; + while (bc_op(*startpc) < BC_FUNCF) startpc--; + setintV(L->top++, (int)(pc - startpc)); + return 2; + } return 1; } return 0; diff --git a/test/tarantool-tests/lj-1472-tracesnap-getpos.test.lua b/test/tarantool-tests/lj-1472-tracesnap-getpos.test.lua new file mode 100644 index 000000000..a56a7a679 --- /dev/null +++ b/test/tarantool-tests/lj-1472-tracesnap-getpos.test.lua @@ -0,0 +1,89 @@ +local tap = require('tap') +local bit = require('bit') +local jutil = require('jit.util') +local vmdef = require('jit.vmdef') + +-- The test checks the optional `getpos` argument of +-- `jit.util.tracesnap()` that makes the function return the PC +-- position of the snapshot in the function bytecode. +-- See also https://github.com/LuaJIT/LuaJIT/issues/1472. + +local test = tap.test('lj-1472-tracesnap-getpos'):skipcond({ + ['Test requires JIT enabled'] = not jit.status(), +}) + +test:plan(8) + +-- Entries in `vmdef.bcnames` are 6 characters long. +local BC_NAME_LENGTH = 6 + +-- Return the name of the bytecode operation for the given +-- instruction, that is encoded in the low byte. +local function opname(ins) + local oidx = BC_NAME_LENGTH * bit.band(ins, 0xff) + local name = vmdef.bcnames:sub(oidx + 1, oidx + BC_NAME_LENGTH) + return (name:gsub('%s+$', '')) +end + +local function payload() + local sum = 0 + for i = 1, 100 do + sum = sum + i + end + return sum +end + +-- Find the loop back-edge (FORL) in the function bytecode and +-- compute its jump target, that is the first instruction of the +-- loop body. `pc == 0` is the JCproto header, so real +-- instructions start from `pc == 1`. JIT compilation is disabled +-- while scanning, so this loop is not recorded as a trace. +jit.off() +local nbc = jutil.funcinfo(payload).bytecodes +local loop_entry +for pc = 1, nbc - 1 do + local ins = jutil.funcbc(payload, pc) + if opname(ins) == 'FORL' then + -- The jump offset is relative to the next instruction and is + -- biased by `BCBIAS_J`. + local jmp = bit.rshift(ins, 16) - 0x8000 + loop_entry = pc + 1 + jmp + break + end +end +test:isnt(loop_entry, nil, 'FORL instruction is found') + +-- Record a root loop trace in `payload`. Snapshot #0 points to +-- the loop entry (the FORL jump target). +jit.on() +jit.flush() +jit.opt.start('hotloop=1') +payload() + +local info = jutil.traceinfo(1) +test:ok(info, 'the root loop trace is recorded') + +local snap, pos = jutil.tracesnap(1, 0, true) +test:istable(snap, 'the snapshot table is returned') +test:is(pos, loop_entry, 'the PC position points to the loop entry') + +local _, pos_without = jutil.tracesnap(1, 0) +test:is(pos_without, nil, 'no PC position without getpos') + +local _, pos_false = jutil.tracesnap(1, 0, false) +test:is(pos_false, nil, 'no PC position with getpos=false') + +local all_valid = true +for sn = 0, info.nexit - 1 do + local _, p = jutil.tracesnap(1, sn, true) + if type(p) ~= 'number' or p < 0 or p >= nbc or + jutil.funcbc(payload, p) == nil then + all_valid = false + end +end +test:ok(all_valid, 'positions of all snapshots are valid offsets') + +test:is(select('#', jutil.tracesnap(1, info.nexit, true)), 0, + 'out-of-range snapshot returns nothing') + +test:done(true) -- 2.51.0