そして最後に、 see
とそのファミリーが、 コンパイル済のコードの表示を行います。 ソース・コード内の一部の内容は、
コンパイルされたコードには存在しません(書式設定やコメント等)。 しかし、 これは、 マクロや Gforth
の最適化機能によってどのようなスレッド化コード(threaded code)やネイティブ・コード(native
code)が生成されるかを確認するのに役立ちます。
see
( "<spaces>name" – ) tools “see”
現在の検索順序(search order)を使用して name を locate
します。 locate
できたら、 その定義を表示します。 これは定義を逆コンパイルすることで実現されるため、 書式は機械化され、
一部のソース情報(コメントや定義内のインタプリトされたシーケンスなど)は失われます。
xt-see
( xt – ) gforth-0.2 “xt-see”
xt で表される定義を逆コンパイルします。
simple-see
( "name" – ) gforth-0.6 “simple-see”
コロン定義 name を逆コンパイルして各セルごとに行表示し、 セルの意味を推測してそれを表示します。
xt-simple-see
( xt – ) gforth-1.0 “xt-simple-see”
simple-see
のようにコロン定義 xt を逆コンパイルします。
simple-see-range
( addr1 addr2 – ) gforth-0.6 “simple-see-range”
[addr1,addr2) ( addr1 <= and < addr2 )の範囲のコードを
simple-see
のように逆コンパイルします
see-code
( "name" – ) gforth-0.7 “see-code”
simple-see
と似ていますが、 インライン化されたプリミティブの動的ネイティブ・コード(dynamic native
code)も表示されます。 静的命令融合(superinstructions)の場合、
最初のプリミティブではなくプリミティブ・シーケンスが表示されます(命令融合(superinstructions)の他のプリミティブも表示されます)。
ネイティブ・コードが生成されるプリミティブの場合、 最初と最後のレジスタ内のスタック項目の数が表示されます(たとえば、 1->1
は、
最初と最後のレジスターに 1 つのスタック項目があることを意味します)。
ネイティブ・コードを使用した各プリミティブまたは命令融合(superinstructions)については、
インライン引数とコンポーネント・プリミティブが最初に表示され、 次にネイティブ・コードが表示されます。
xt-see-code
( xt – ) gforth-1.0 “xt-see-code”
コロン定義 xt を see-code
のように逆コンパイルします。
see-code-range
( addr1 addr2 – ) gforth-0.7 “see-code-range”
[addr1,addr2) ( addr1 <= and < addr2 )の範囲のコードを
see-code
のように逆コンパイルします。
例として、 以下について考えてみましょう:
: foo x f@ fsin drop over ;
この例は特に役に立つわけではありませんが、 さまざまなコード生成の違いを示しています。 これを AMD64 の gforth-fast
でコンパイルし、 see-code foo
を使用すると以下の出力が得られます(訳注: OSのコマンドラインから、
gforth
ではなくて gforth-fast
で起動する):
$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
まず、 コンポーネント lit
と f@
を含む静的命令融合(superinstruction)のスレッド化コード・セルが表示されます。 それは、 レジスター内の 1 つのデータ・スタック項目
(1->1
) で始まり・終わります。 )。 これに、lit
の引数 x
のセルと、 命令融合の
f@
コンポーネントのセルが続きます。 後者のセルは使用されませんが、 Gforth 内部の理由により存在します。
その次に、 命令融合 lit f@
に対して動的に生成されたネイティブ・コードを表示します。 注意: アドレスを比較するとわかるように、
このネイティブ・コードはメモリー内のスレッド化コードと混合されていない(not mixed)ことに注意してください。
ここに表示されているネイティブ・コードを理解するには、以下のレジスターについて知っておくべきです:
スレッド化コードの命令ポインターは
r13
データ・スタック・ポインターは r15
最初(TOS)のデータ・スタック・レジスターは r8
(データ・スタックのTOS、 つまり、 レジスターにデータ・スタック項目が 1 つある場合、 スタックのTOSがそこに存在します)
リターン・スタック
ポインターは rbx
FP スタック・ポインターは r12
FP スタックの先頭(TOS)は
xmm15
注意: レジスターの割り当てはエンジンによって異なるため、
コード表示においては異なるレジスターの割り当てが表示される場合があることに注意してください。
lit f@
の動的ネイティブ・コードは、 ディスパッチ・ジャンプ(dispatch jump;別名 NEXT)(訳注:
古典的には内部インタープリターへ制御を戻すためのジャンプ)で終了します。 これは、 定義内の次のワード fsin
のネイティブ・コードは動的に生成しないためです。
次に、 fsin
のスレッド化コード・セルが表示されます。 このワードには動的に生成されるネイティブ・コードはなく、
see-code
では静的ネイティブ・コードは表示されません(fsin
の静的ネイティブ・コードを確認したければ
see fsin
)。 gforth-fast
内の静的ネイティブ・コードを含むすべてのワードと同様、
データ・スタック表現への影響は 1->1
(gforth
エンジンでは 0->0
) です。 しかし、
これは表示されません。
次に、 drop
のスレッド化コード・セルが表示されます。 ここで使用されるネイティブ・コード版のバリエーションは、 レジスター内の 1
つのデータ・スタック項目で始まり、 レジスター内の 0 個のデータ スタック項目で終わります(1->0
)。 この後に
drop
のこのバリエーションのネイティブ・コードが続きます。 ここのネイティブ・コードは次のワードのコードに続くため、 ここには
NEXT はありません。
次に、 over
のスレッド化コード・セルと、 その後に 0->1
バリエーションで動的に生成されたネイティブ・コードが表示されます。
最後に、 ;s
のスレッド化されたネイティブ・コード(foo
内の ;
用にコンパイルされたプリミティブ)が表示されます。 ;s
は制御フローを実行する(制御フローからリターンする)ため、 NEXT
で終了する必要があります。