GAME68クロスコンパイラを書いてみた(1) (ベーシックマスター開発 その20)
GAME68は移植できたので、順番からすると次はGAME68コンパイラの移植になるのだが、どうも気乗りしない。
変数領域を0ページ外に移動したので、コンパイラの修正が必要になる。いまさらベーシックマスターの狭い画面(32×24)で、vi以外のエディタで作業するのは耐えられない。
ソースをPC側で作っても、ソースの移動とコンパイル・確認はエミュレーター側での作業になるので、手間がかかりすぎる。
(そもそもお試し用であれば、過去に移植されたGAME-MB用のコンパイラがあるのでそれ使えばいいし)
どうせなら、コンパイラ作成の勉強も兼ねてクロスコンパイラを作ってみることにした。ホストはMacOSで、オブジェクトとして6800のアセンブリコードを出力する。アセンブラはa09を使う。
ある程度動くものができました。
大昔にコンパイラはいくつか書いたし、ソースコードも読んだことがある。ただ、そのころのコンパイラはメモリ不足もあって、満足な最適化は行えなかった。ソースを読んでいきなりバイナリを書くものが大半だし。
せっかくなので、現代的なコンパイラの勉強もしたい。みんなが参考にしている Rui Ueyama さんの記事を参考にして書いてみた。
手元の記録だと2024年7月24日に作成開始しているので、2週間ほどかかったわけだ。GAME68の言語仕様やMC6800の細かい動作を忘れていて、再確認しながらなので時間がかかっている。
作り直した方がいいな、これ
GAMEはメモリが少ない時代に作られた言語なので、GAMEで書かれたソースも省メモリに書けるようになっている。そこの処理が難しい。
例えば、文字列表示の "…." は、直前直後に / (改行命令)をくっつけて書いても動く。 /"READ ME"/ ← こんな感じ。
これの解析が大変、というか、コンパイラを作ってしまってから気がついたので、対応不能になってしまった。潰しきれない非互換として残っている。
他にも、参考にした 9cc はCなので行に依存しないけど、GAMEはVTL/BASIC系の言語なので行番号があり、行単位に処理を行っている。
当然、解析時は行の解析を先にすべきだが、それも出来ていない。
コンパイル処理は ソースコード → トークン列 → 中間言語 → オプティマイズ → コード生成 の順に行っているが、現代的なコンパイラのように中間言語を2段階にした方がよさそうである(牛刀っぽいが)。
割り算の副作用の処理が難しい
例えば、GAMEの割り算は、自動的に剰余を MOD変数(%)に代入するようになっている。このため、割り算を単純に最適化することができない。
割り算が含まれている式は順序変更が難しいし、いちいち剰余をMOD変数に代入するのも無駄である。
例。 PIの計算で、 I=%(0/1),L という式を使っている。これは for i=0/1 to L のことなんだけど、0ではなくて0/1になっているのがミソ。これでMOD変数を0クリアしている。
仕方ないのでコンパイル時に陽にMODに0を入れて、そのあとでI=0としている。ほとんどの計算ではMODはその場で消費するので無駄なのだが、仕方ない。
LDX #0
STX _MOD
CLRB
CLRA
STAB _I+1
STAA _I
LDX _L
STX .LT_I
これ、中間言語を頑張ってフロー解析すれば消せるはずだけど、この版の構造だと無理なはず。要作り直し。
ベンチマーク
昔なつかしいASCIIベンチマーク1-7と、素数表・πの計算をしてみました。
頑張って最適化したつもりなのに、TK-80BS@2MHzと良い勝負なのは、乗除算ルーチンが律速だからでしょうか。GAME-80コンパイラは200行未満なのに、こんなに速いのは驚異です。
ベンチマーク3番は A=K/K*K+K-K を、4番は A=K/2*3+4-5 の計算を繰り返すものなので、 定数掛け算をシフトに置き換えできる4番が速いです。
GAME-80/MZは素直に掛け算しているので、差が出ていせん。
インタプリタでは逆に、10進変換を毎回行う4番の方が遅いのは面白いところです。
追記: TK-80BS版に負けてる項目があると悔しいので、高速化してみました(2024/8/10)。
処理系 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 備考 |
---|---|---|---|---|---|---|---|---|
GAME68 | 1.55 | 4.60 | 9.85 | 10.30 | 13.50 | 22.36 | 29.16 | インタプリタ |
GAME68-CC1 | 0.08 | 0.11 | 1.06 | 0.66 | 0.68 | 1.01 | 1.40 | コンパイラ |
GAME68-CC1 | 0.06 | 0.05 | 0.78 | 0.48 | 0.50 | 0.81 | 1.15 | 高速化版(2024/8/10) |
GAME80 Compiler | 0.08 | 0.08 | 1.21 | 1.26 | 1.28 | 1.68 | 1.96 | ASCII 1979/7 |
GAME-MZ Compiler | 0.1 | 0.1 | 1.7 | 1.7 | 1.8 | 2.1 | 2.3 | ASCII 1979/10 |
処理系 | prime | pi100 | pi200 | 備考 |
---|---|---|---|---|
GAME68 | 21.73 | 149.91 | 586.53 | インタプリタ |
GAME68-CC1 | 2.93 | 28.98 | 111.28 | コンパイラ |
GAME68-CC1 | 2.53 | 23.43 | 90.10 | 高速化版 |
githubに載せました
区切りとして github に置きました。この版はこれで終わりにして、第2版を作ろう
ディスカッション
コメント一覧
まだ、コメントがありません