Re: Understanding SNAP

  • From: Peter Cawley <corsix@xxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Tue, 26 Sep 2017 23:42:56 +0100

On Tue, Sep 26, 2017 at 9:48 AM, Raj <rajlistuser@xxxxxxxxx> wrote:

Just to clarify: Is the number of CPU registers determined by the
actual CPU architecture?

Yes. For example, on x64, you have 16 integer registers and 16
floating-point registers to play with (with some caveats: one of those
integer registers is always the C stack pointer, and in LJ_GC64 mode,
one of those integer registers is always a pointer to LuaJIT's
dispatch table).

ie. same code/luajit combination which spills
registers in a CPU with less number of registers will not spill
registers in a CPU with more number of registers? Consequently
register coalescing  NYI in one CPU may disappear in another CPU with
more number of registers?

In theory yes, but e.g. all x64 CPUs have the same number of registers
(at least in the ISA, which is what matters).

Can you please shed some light on "shuffle things around" and layout
desired by the side trace" part?

I'll use the following example, as I have it to hand:

---- TRACE 1 IR
....              SNAP   #0   [ ---- ---- ]
0001 rbp      int SLOAD  #8    CI
0002 xmm7     num CONV   0001  num.int
0003 xmm3   + num ADD    0002  +1.5
0004 xmm4   + num ADD    0002  +2.5
0005 xmm5   + num ADD    0002  +3.5
0006 xmm6   + num ADD    0002  +4.5
0007 xmm7   + num ADD    0002  +5.5
....              SNAP   #1   [ ---- ---- ---- 0003 0004 0005 0006
0007 0001 ---- ---- ---- ]
...

---- TRACE 2 start 1/1 code:4
0014  UNM      1   1
0015  JFORL    6   1
---- TRACE 2 IR
0001 xmm7     num SLOAD  #3    PI
0002 xmm4     num SLOAD  #4    PI
0003 xmm5     num SLOAD  #5    PI
0004 xmm6     num SLOAD  #6    PI
0005 xmm0     num SLOAD  #7    PI
0006 rbp      int SLOAD  #8    PI
...

Based on these SLOADs, the start of trace 2 wants xmm7 to contain the
result of SLOAD #3, which based on trace 1 snap 1 (noting that trace 2
starts at trace 1 exit 1) is trace 1's instruction 0003, which is in
xmm3 at the time of trace 1 snap 1, hence LuaJIT needs to do "mov
xmm7, xmm3" at the start of trace 2. The start of trace 2 also wants
xmm0 to contain #7, which is 0007 based on the snap, which is in xmm7
(that these numbers line up is purely a coincidence of the example at
hand) at the time of trace 1 snap 1. Hence LuaJIT also needs to do
"mov xmm0, xmm7" at the start of trace 2. Crucially, it needs to do
"mov xmm0, xmm7" before it does "mov xmm7, xmm3" (as otherwise it
would overwrite xmm7 before it had pulled the value out). As it
happens, trace 2 wants SLOAD 4 though 6 in xmm4 through xmm6, and
these end up being 0004 through 0006, which trace 1 already had in
xmm4 through xmm6, so no shuffling was necessary for them (this is not
pure chance). In general, there will be some collection of
register-to-register moves, C-stack-to-C-stack moves, and
register/C-stack moves which need to happen, and various constraints
on which moves have to happen before which other moves. LuaJIT's
current algorithm for scheduling all of these moves is quite simple,
and cannot handle various cases.

Does Each filename:num| separation represents one stack frame from the
function at that filename:num called previously?

Yes, frames are marked with vertical bars. The `filename:num` isn't
part of the marker though; it denotes a value to be restored to the
stack if the snapshot exit is taken (namely, it restores what I've
been referring to as a call frame metadata slot).

Also what does the +1 and +4 denote?

The integer 1 and the integer 4. For example, "+1" means that there
would have been an SSTORE which stored the constant integer 1 to a
particular Lua stack slot; continuing the theme that every entry in a
SNAP represents an SSTORE, sometimes the thing being stored is the
result of an IR instruction, and sometimes the thing being stored is a
constant (`filename:num` counts as a kind of constant).

Other related posts: