Next: , Previous: , Up: Gforth locals   [Contents][Index]


6.22.1.5 Locals implementation

Gforth は追加のローカル変数用スタック(ローカル・スタック)を使用します。 この最も説得力のある理由は、 リターン・スタックが浮動小数点数に整列されていないことです。 この追加のスタックを使用すると、 リターン・スタックをローカル変数用スタックとして使用する場合の問題や制限も解消されます。 他のスタックと同様に、 ローカル・スタックは下位アドレスに向かって成長します。 いくつかのプリミティブにより効率的な実装になっています。 あなたは、 これらを直接使用するべきではありませんが、 see の出力には表示されるため、 以下に文書化しておきます:

@localn ( noffset – w ) gforth-internal “fetch-local-n”
\ 訳注: ローカル・スタックのTOSを0として noffset 番目のコピーをデータ・スタックに積む
f@localn ( noffset – r ) gforth-1.0 “f-fetch-local-n”
lp@ ( – c-addr ) gforth-0.2 “lp-fetch”

C_addr は、 ローカル・スタック・ポインタの現在の値です。

doc-lp+!#(訳注: まだ説明書いて無いっぽい)

lp! ( c-addr – ) gforth-internal “lp-store”
>l ( w – ) gforth-0.2 “to-l”
\ 訳注: w をローカル・スタックにプッシュ
f>l ( r – ) gforth-0.2 “f-to-l”

これらのプリミティブに加えて、 一般的に発生するインライン引数に対するこれらのプリミティブのいくつかの特殊化が、 効率上の理由から提供されています(例: 0 @localn の特殊化として @local0 )。 以下のコンパイル・ワード(compiling words)は、 適切な特殊バージョン、 または一般バージョンを適切にコンパイルします(訳注: @local0 シリーズは、 @local0 ローカル・スタックのTOS(のコピー)をスタックに積む、 @local1 ローカル・スタックの2nd(のコピー)をスタックに積む、 〜 @local4 まである):

compile-lp+! ( n –  ) gforth-0.2 “compile-l-p-plus-store”

?branch-lp+!# のような、 条件分岐と lp+!# の組み合わせ(ローカル・スタック・ポインタは分岐が選択された場合にのみ変更されます)は、 ループの効率と正確性のために提供されています。

ディクショナリ空間内の特別な領域が、 ローカル変数名を保持するために予約されています。 {: はディクショナリ・ポインタをこの領域に切り替え、 :} はそれを元に戻し、 ローカル変数の初期化コードを生成します。 W: などは通常の定義ワードです。 この特別な領域は、 すべてのコロン定義の先頭でクリアされます。

Gforth のディクショナリの特別な機能は、 型指定子なしでローカルの定義を実装するために使用されます。 すべてのワードリスト(別名ボキャブラリ)には、 検索などのための独自のメソッド(methods)があります (see Word Lists)。 型指定子なしでローカルの定義を実装するという目的のために、 私達は特別な検索メソッドを使用してワードリストを定義しました。 ワードが検索されると、 実際には W: を使用してそのワードが作成されます。 {: は、 最初に :}W: などを含むワードリストで検索し、 次に型指定子のないローカル変数を定義するためのワードリストで検索するよう検索順序スタック(the search order)を変更します。

ライフタイム・ルールは、 コロン定義内のスタック規律(stack discipline)をサポートします。 ローカル変数のライフタイムは、 他のローカル変数のライフタイムと入れ子になっているか、 他のローカル変数のライフタイムと重ならないか、 です。

BEGINIFAHEAD では、 ローカル・スタック・ポインター操作のコードは生成されません。 制御構造のワード間で、 ローカル変数定義はローカル変数をローカル・スタックにプッシュできます。 AGAIN は、 他の 3 つの制御フローワードとの中で最も単純です。 分岐する前に、 対応する BEGIN のローカル・スタックの深さを復元する必要があります。 そのコードは以下のようになります:

lp+!# current-locals-size - dest-locals-size
branch <begin>

UNTIL はもう少し複雑です。 分岐して戻る場合は、 AGAIN と同じようにローカル・スタックを調整する必要があります。 ただし、 戻らずにその後ろへ流れる場合は、 ローカル・スタックを変更してはなりません。 コンパイラは以下のコードを生成します:

?branch-lp+!# <begin> current-locals-size - dest-locals-size

ローカル・スタック・ポインタは、 分岐が行われた場合にのみ調整されます。

THEN は、 やや非効率なコードを生成する可能性があります:

lp+!# current-locals-size - orig-locals-size
<orig target>:
lp+!# orig-locals-size - new-locals-size

2 番目の lp+!# は、 ローカル・スタック・ポインタを orig 時点のレベルから THEN の後のレベルに調整します。 最初の lp+!# は、 ローカル・スタック・ポインタを現在のレベルから orig 時点のレベルに調整するため、 完全な効果は、THEN の後の現在のレベルから正しいレベルに調整されることになります。

従来の Forth の実装では、 dest 制御フロー・スタック・エントリはターゲット・アドレスにすぎず、 orig エントリはパッチ当てされるアドレスにすぎません。 ローカル変数の実装は、 すべての orig または dest 項目にワードリストを追加します。 これは、 エントリによって記述された時点で可視である(または可視である想定される)ローカル変数のリストです。 私たちの実装では、 エントリの種類を識別するためのタグも追加します。 特に、 生きているのと死んでいるエントリ(到達可能なエントリと到達不可能なエントリ)を区別するためです。

ローカル変数のワードリストに対して、 いくつかの珍しい操作を実行する必要があります:

common-list ( list1 list2 – list3  ) gforth-internal “common-list”
sub-list? ( list1 list2 – f  ) gforth-internal “sub-list?”
list-size ( list – u  ) gforth-internal “list-size”

ローカル変数のワードリスト実装のいくつかの機能により、 これらの操作の実装が簡単になります。 ローカル変数のワードリストはリンクされたリストとして編成されます。 リストに同一のローカル変数が含まれている場合、 これらのリストの末尾は共有されます。 名前のアドレスは、 リスト内でその後ろにある名前のアドレスよりも大きくなります。

もう 1 つの重要な実装の詳細は、 変数 dead-code です。これは、 BEGINTHEN によって直接到達できるか、 解決するブランチ経由でのみ到達できるかを判断するために使用されます。 dead-codeUNREACHABLEAHEADEXIT などによって設定され、 コロン定義の先頭 や BEGIN や 通常は THEN によってクリアされます。.

カウンタ付きループはほとんどの点で他のループと似ていますが、 LEAVE には特別な注意が必要です。 基本的に AHEAD と同じサービスを実行しますが、 制御フロー・スタック・エントリは作成されません。 したがって、 情報は別の場所に保存する必要があります。 従来、 情報は LEAVE によって作成されたブランチのターゲット・フィールドに、 これらのフィールドをリンク・リストに編成することによって格納されていました。 残念ながら、 この巧妙なトリックでは、 拡張制御フロー情報を保存するための十分なスペースが提供されません。 したがって、 別のスタックである Leave スタックを導入します。 これには、 すべての未解決の LEAVE の制御フロー・スタック・エントリが含まれています。

ローカル変数名は、 どの制御フロー経路にも表示されなくなった場合でも、 コロン定義の終わりまで保持されます。 場合によっては、 これによりローカル変数の名前領域に必要な領域が増加する可能性がありますが、 通常はこの領域を再利用するよりもコード量にかかるコストは少なくなります。


Next: Closures, Previous: Locals programming style, Up: Gforth locals   [Contents][Index]