- 追加された行はこのように表示されます。
- 削除された行は
このように表示されます。
私の実装したForth、moiforthは、内部インタプリタをスイッチスレッディングで実装している。
switch (prog[prog_cnt]) {
case CODE_PUSH:
処理
break;
case CODE_PLUS:
処理
break;
これを、コールスレッディングに変える。
コールスレッディングに変えると、ワード処理は個別の関数に分けられ、それを呼び出すのは次のような処理になる:
while (1) {
(*prog[prog_cnt])();
}
なにがうれしいかと言うと、
* 内部インタプリタがコンパクトになる
* 処理が個々の関数に分けられ、すっきりする
ただ、うれしくない部分もあり、
* 遅くなる。スイッチスレッディングは分岐だが、コールスレッディングはサブルーチンコールになるため
いろいろ悩んだが、コールスレッディングにした。
なぜなら、まだ書いたことがない、から。
スレッディングについては、以下に詳しく書かれている。
* [[ThreadedCode]]
* [langsmith:266|http://www.atdot.net/~ko1/w3ml/w3ml.cgi/langsmith/msg/266]の前後の話
* [YARV Maniacs 【第 3 回】 命令ディスパッチの高速化|http://jp.rubyist.net/magazine/?0008-YarvManiacs]
上記のコールスレッディングは、直接スレッディングなので、ジャンプ先アドレスがそのまま入っているが、これから作るのは間接スレッディングなので、以下のようになる。
while (1) {
(*jump_table[prog[prog_cnt++]])();
}
当然直接スレッディングより遅い。
しかし、prog[]のコードを番地のサイズより小さくすれば、メモリ効率が良くなる。
PCではそんなことは関係ないが、組み込み用途だと意味がある。かも。
実際のコードは、こうなった。
while (prog[prog_cnt] != CODE_END) {
(*jump_table[prog[prog_cnt++]])();
}
ループを抜けるには、プログラムの終了判定をしなければならない。
アセンブラならループの外にジャンプするだけで済むが、コールスレッディングだと、こうせざるを得ない。
そのぶん遅くなる。
つぎに、ワードの処理を関数単位に分ける。doを例にすると、こうなる。
/**
* ワード'do'を実行する
*/
void call_do(void)
{
loop_pos--;
/* スタックの内容をループスタックに入れる */
loop_cnt_stack[loop_pos] = pop();
loop_end_stack[loop_pos] = pop();
}
さらに、ジャンプ先となる関数ポインタの配列を作った。
void (*jump_table[])(void) = { /**< ワードのジャンプ先テーブル */
NULL, call_push, plus, print, word_if, word_else, word_then, call_do,
call_loop, call_i, call_cr, call_word, proc_ret
};
新しいスレッディングのmoiforthを、テストする。
testfile以下のプログラムを次々テストしていくが、問題ない。
というわけで、スイッチスレッディングからコールスレッディングの変更が終わった。
[ソースコード|http://moi2.sakura.ne.jp/fswiki/wiki.cgi?page=Forth%A4%F2%BA%EE%A4%C3%A4%C6%A4%DF%A4%EB+%2F+%A5%B9%A5%EC%A5%C3%A5%C7%A5%A3%A5%F3%A5%B0%A4%CE%CA%D1%B9%B9&file=moiforth%2D0%2E8%2Ezip&action=ATTACH]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / コンソールの複数行入力対応]] [[次へ|aaa]]
[[戻る|Forthを作ってみる]] [[前へ|Forthを作ってみる / コンソールの複数行入力対応]] [[次へ|Forthを作ってみる / 文字列を表示する]]
{{adsence}}