6.26.4 Examining compiled code

And finally, see and friends show compiled code. Some of the things in the source code are not present in the compiled code (e.g., formatting and comments), but this is useful to see what threaded code or native code is produced by macros and Gforth’s optimization features.

see ( "<spaces>name" –  ) tools “see”

Locate name using the current search order. Display the definition of name. Since this is achieved by decompiling the definition, the formatting is mechanised and some source information (comments, interpreted sequences within definitions etc.) is lost.

xt-see ( xt –  ) gforth-0.2 “xt-see”

Decompile the definition represented by xt.

simple-see ( "name" –  ) gforth-0.6 “simple-see”

Decompile the colon definition name, showing a line for each cell, and try to guess a meaning for the cell, and show that.

xt-simple-see ( xt –  ) gforth-1.0 “xt-simple-see”

Decompile the colon definition xt like simple-see

simple-see-range ( addr1 addr2 –  ) gforth-0.6 “simple-see-range”

Decompile code in [addr1,addr2) like simple-see

see-code ( "name" –  ) gforth-0.7 “see-code”

Like simple-see, but also shows the dynamic native code for the inlined primitives. For static superinstructions, it shows the primitive sequence instead of the first primitive (the other primitives of the superinstruction are shown, too). For primitives for which native code is generated, it shows the number of stack items in registers at the beginning and at the end (e.g., 1->1 means 1 stack item is in a register at the start and at the end). For each primitive or superinstruction with native code, the inline arguments and component primitives are shown first, then the native code.

xt-see-code ( xt –  ) gforth-1.0 “xt-see-code”

Decompile the colon definition xt like see-code.

see-code-range ( addr1 addr2 –  ) gforth-0.7 “see-code-range”

Decompile code in [addr1,addr2) like see-code.

As an example, consider:

: foo x f@ fsin drop over ;

This is not particularly useful, but it demonstrates the various code generation differences. Compiling this on gforth-fast on AMD64 and then using see-code foo outputs:

$7FD0CEE8C510 lit f@     1->1 
$7FD0CEE8C518 x
$7FD0CEE8C520 f@
7FD0CEB51697:   movsd   [r12],xmm15
7FD0CEB5169D:   mov     rax,$00[r13]
7FD0CEB516A1:   sub     r12,$08
7FD0CEB516A5:   add     r13,$18
7FD0CEB516A9:   movsd   xmm15,[rax]
7FD0CEB516AE:   mov     rcx,-$08[r13]
7FD0CEB516B2:   jmp     ecx
$7FD0CEE8C528 fsin
$7FD0CEE8C530 drop    1->0 
7FD0CEB516B4:   add     r13,$08
$7FD0CEE8C538 over    0->1 
7FD0CEB516B8:   mov     r8,$10[r15]
7FD0CEB516BC:   add     r13,$08
$7FD0CEE8C540 ;s    1->1 
7FD0CEB516C0:   mov     r10,[rbx]
7FD0CEB516C3:   add     rbx,$08
7FD0CEB516C7:   lea     r13,$08[r10]
7FD0CEB516CB:   mov     rcx,-$08[r13]
7FD0CEB516CF:   jmp     ecx

First, you see a threaded-code cell for a static superinstruction with the components lit and f@, starting and ending with one data stack item in a register (1->1); this is followed by the cell for the argument x of lit, and the cell for the f@ component of the superinstruction; the latter cell is not used, but is there for Gforth-internal reasons.

Next, the dynamically generated native code for the superinstruction lit f@ is shown; note that this native code is not mixed with the threaded code in memory, as you can see by comparing the addresses.

If you want to understand the native code shown here: the threaded-code instruction pointer is in r13, the data stack pointer in r15; the first data stack register is r8 (i.e., the top of stack resides there if there is one data stack item in a register); the return stack pointer is in rbx, the FP stack pointer in r12, and the top of the floating-pont stack in xmm15. Note that the register assignments vary between engines, so you may see a different register assignment for this code.

The dynamic native code for lit f@ ends with a dispatch jump (aka NEXT), because the code for the next word fsin in the definition is not dynamically generated.

Next, you see the threaded-code cell for fsin. There is no dynamically-generated native code for this word, and see-code does not show the static native code for it (you can look at it with see fsin). Like all words with static native code in gforth-fast, the effect on the data stack representation is 1->1 (for gforth, 0->0), but this is not shown.

Next, you see the threaded-code cell for drop; the native-code variant used here starts with one data stack item in registers, and ends with zero data stack items in registers (1->0). This is followed by the native code for this variant of drop. There is no NEXT here, because the native code falls through to the code for the next word.

Next, you see the threaded-code cell for over followed by the dynamically-generated native code in the 0->1 variant.

Finally, you see the threaded and native code for ;s (the primitive compiled for ; in foo). ;s performs control flow (it returns), so it has to end with a NEXT.