私の実装した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を作ってみる / コンソールの複数行入力対応]] [[次へ|Forthを作ってみる / 文字列を表示する]] {{adsence}}