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.