Re: Understanding SNAP

  • From: Peter Cawley <corsix@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Mon, 25 Sep 2017 21:47:02 +0100

On Mon, Sep 25, 2017 at 9:05 AM, Raj <rajlistuser@xxxxxxxxx> wrote:

1.  IR instruction number: First column of IR, increments continuously
by one per every instruction.

Yes, noting one per IR instruction rather than one per original
bytecode instruction.

2. Argument to SLOAD: start at #0 (base frame, or call frame
metadata), #1: first slot in the first frame, starting at function
argument. In luajit wiki, it is defined as "(register 0 in the
bytecode)"

What is the register 0 mentioned here?

Register 0 is another term for stack slot zero. If your function has
arguments, it is the first argument. Otherwise, if your function has
local variables, it is the lexicographically earliest local which is
still in scope.

3. Instruction operands of IR. eg:  in "0003 xmm7   + num MUL    0002
-3" what does 0002 denote? Is it a reference to a memory
location/register etc, or to an instruction?

In this case, 0002 refers to the result of instruction 0002 (given the
SSA form of the IR, instructions are synonymous with their results).

Ok. One point I am missing here is the correspondence between 0 in
"0006  MULVN    0   0   1  ; -3" and 0002 in 0003 xmm7   + num MUL
0002  -3" It probably reflects my confusion about the operands of IR
instruction.

Bytecode is typically printed as "OFFSET NAME DST SRC1 SRC2 ;
COMMENT". In this case, NAME is MULVN, DST is 0, SRC1 is 0, and SRC2
is 0. IR is typically printed as "... type NAME SRC1 SRC2". In this
case, NAME is MUL, SRC1 is 0002, and SRC2 is -3. Having matched MULVN
to MUL, the SRC1 and SRC2 refer to the same thing (and the "VN" suffix
of "MULVN" indicates that in the bytecode, SRC1 is a stack slot index,
and SRC2 is a numeric constant index).

SNAP #3 exits after the loop has finished, but did you mean SNAP #1
exits before the loop has finished?

No; #1 and #3 both exit to the same place in the bytecode (as a
general rule, each snapshot after "--- LOOP ---" is a copy of one of
snapshots before "--- LOOP ---", and will exit to the same place). You
can infer where they exit to by looking at the instruction which
follows them, which is "LE". The snapshot is taken if the instruction
fails, and if this particular "LE" fails, it means that the loop
condition is false, i.e. execution continues from immediately after
the loop.

Can we figure that out by waking backwards from the last trace and
looking at the trace start line?

If the exit becomes warm and causes a side trace, then yes, the start
location of the side trace tells you the exit location of the
snapshot.

SLOADs in traces leading to register coalescing too complex trace has
flag PI. What does it mean by "Coalesce with parent trace"

An SLOAD conceptually represents loading a value from the Lua stack.
In root traces, it always is loading a value from the Lua stack. In
side traces, it can end up meaning "load the value which would have
been in the Lua stack, had the parent trace actually actually done the
SSTOREs which were implied by the snapshot leading to the side trace"
(again, SSTOREs aren't a thing, and don't happen, but you can think of
SNAPs as indicating which SSTOREs would have happened, if they were a
thing). Stores to the Lua stack do not happen in between the parent
trace and a side trace - instead LuaJIT combines the hypothetical
SSTORE and the actual SLOAD into a single register-to-register move.
That said, if values are in C stack spill slots rather than CPU
registers, an additional load and/or store can be required on either
side of the move. At a slightly higher level, you can consider the
register / C stack layout at the exit point in the parent trace, and
the register / C stack layout at the entry point in the side trace,
and LuaJIT's job is to shuffle things around in order to reach the
layout desired by the side trace, starting from the layout in the
parent trace. The current shuffling algorithm is overly simplistic,
and the corresponding NYI aborts indicate that a more complex
algorithm is required.

Also In the original IR

---- TRACE 1 start stdin:1
0006  MULVN    0   0   1  ; -3
0007  FORL     1 => 0006
---- TRACE 1 IR
....              SNAP   #0   [ ---- ]
0001 rbp      int SLOAD  #2    CI
0002 xmm7  >  num SLOAD  #1    T

The SNAP has just one entry, while SLOAD loads #2 and #1. How is this 
possible?

A SNAP indicates the SSTOREs which would have occurred preceding the
SNAP, if there were SSTOREs in the IR. As #0 is at the start of the
IR, nothing precedes it, so it is empty. An SLOAD is not an SSTORE, so
it does not really interact with snapshots. An SLOAD indicates a load
from the stack rather than a load from any preceding snapshot, so the
number of entries in a preceding snapshot bears no resemblance to the
stack slot index in an SLOAD. In fact, you should always see that the
index referred-to in an SLOAD is either "----" or not present in the
most recent preceding snapshot - if it was anything else, then there
would already be an IR instruction giving the value of that stack
slot, and so an SLOAD would not be required.

Other related posts: