Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Kaplun via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Sergey Bronnikov <estetus@gmail.com>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit] Optionally return PC position in jit.util.tracesnap().
Date: Tue, 13 Oct 2026 16:41:02 +0300	[thread overview]
Message-ID: <as407pAECYT259Ci@root> (raw)
In-Reply-To: <befa92e38394efa80b1b77d136c2c3d3ff39eb25.1791459869.git.estetus@gmail.com>

Hi, Sergey!
Thanks for the patch!
LGTM, after fixing my comments below.

On 08.10.26, Sergey Bronnikov wrote:
> From: Mike Pall <mike>
> 
> 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.

I don't understand this paragraph. Usually we don't describe added tests
for the features (especially in the way "what was done" instead of "why
it was done").

> 
> 1. https://github.com/ligurio/ljopt
> 

Please, mention the backporting issues as well.

> 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

<snipped>

> 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+$', ''))

Minor: I suggest the more readable:
| return name:match('%a+')
Feel free to ignore.

> +end
> +
> +local function payload()
> +  local sum = 0
> +  for i = 1, 100 do

Minor: Typo: s/100/4/

> +    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

Typo: s/that/which/

> +-- 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

Let's name the magic number:
| local BCBIAS_J = 0x8000

> +    loop_entry = pc + 1 + jmp
> +    break
> +  end
> +end
> +test:isnt(loop_entry, nil, 'FORL instruction is found')

It should be an assert instead, since this is the test precondition.

> +
> +-- 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')

It should be an assert instead, since this is the test precondition.

> +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')
> +

It would be nice to add any correctness test.
The most trivial in mind is the following:

| src/luajit -Ohotloop=1 -e '
|   local jutil = require"jit.util"
|   local function trivial()
|     local a = 0
|     return a
|   end
|   trivial()
|   trivial()
|
|   local _, b = jutil.tracesnap(1, 0, true)
|   print(b)
|   local _, b = jutil.tracesnap(1, 1, true)
|   print(b)
| '
| 1
| 2

The snapshot is emitted at the function entry and before returning the
result which makes it trivial to detect:

| ---- TRACE 1 start (command line):3
| 0001  KSHORT   0   0
| 0002  RET1     0   2
| ---- TRACE 1 IR
| ....        SNAP   #0   [ ---- ]
| ....        SNAP   #1   [ ---- +0   ]

Don't forget to reset the hotcounters during the recording of this
example.

> +test:done(true)
> -- 
> 2.51.0
> 

-- 
Best regards,
Sergey Kaplun

      reply	other threads:[~2026-10-13 13:41 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08 11:44 Sergey Bronnikov via Tarantool-patches
2026-10-13 13:41 ` Sergey Kaplun via Tarantool-patches [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=as407pAECYT259Ci@root \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=estetus@gmail.com \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH luajit] Optionally return PC position in jit.util.tracesnap().' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox