chibicc compiler を6800向けに改造する (5) アドレッシングと最適化
MC6800向けにコンパイラを書いていて困るのは、Acc A,B と IX レジスタ と SP が別世界の住人であることだ。
Acc A,B で演算した結果をIXやSPに移動するには、ゼロページを経由するしかない。12cycles, 6bytes必要だ。遅い。
スタック経由でも渡せるが、その場合はさらに時間がかかる。
STAB @tmp+1 STAA @tmp LDX @tmp
MC6809やMC6803なら悩まなくて済む
MC6809はTFR命令でレジスタ間の移動は自由にできるし、LEAX命令で加算も可能だ。
もちろん、最適化を極めれば苦労はあるのだが、そこそこの労力でそれなりのコードが生成できる。MC6800とは大違いである。
MC6809用のコンパイラである acwjも、アドレス生成には苦労しているが、MC6800だともっと大変。
MC6800用のコード生成に必要なもの
Acc A,B と IX レジスタが別世界の住人であることを意識したコード生成が必要である。
例えば、このようなコードだ。変数に値を代入するのだが、それぞれ違うコードを作る必要がある。
int g; int h[10]; void func(int x) { int a; int b[10]; g = 0; g = 複雑な式; h[0] = 0; h[0] = 複雑な式; h[x] = 0; h[x] = 複雑な式; a = 0; a = 複雑な式; b[0] = 0; b[0] = 複雑な式; b[x] = 0; b[x] = 複雑な式; }
右辺がIXレジスタを使わない場合
0ならCLR命令が使えるし、定数ならLDAB #定数下位バイト / LDAA #定数上位バイト で良い。面倒なので、以下 LDD と書くことにする。
左辺がアドレス計算を伴う式であっても、結果がIX+offsetで返されれば次のコードで生成できる。
(左辺のアドレスを生成してIXに入れる) LDD #定数 (あるいはIXを使わない演算) STD off,X
配列でないローカル変数や、添え字が定数であるローカル配列は上記パターンで良い。グローバル変数も同様。
IXレジスタだけを使って redeference できる場合も同様である。
左辺がAcc A,Bを使わない場合
A = expr; や A[const] = expr; のような代入は、左辺のアドレスを求めるのにAcc A,Bを使わなくて済む。
このような場合は以下のようにコードを生成する。
右辺を計算する。IXを壊して良い。結果はAcc A,B Xに左辺のアドレスを入れる STD off,X
左辺がグローバル変数であれば、絶対アドレスで STD adrs+off になるが、大差はない。
左辺と右辺で壊れるレジスタをあらかじめ知っておく必要がある
どのレジスタが保存されて、どのレジスタが保存されないかを、オブジェクトコードを生成する前に知る必要がある。
オブジェクトを生成しながら調べると、AccABやIXの待避/復旧が発生して効率が悪い。
両辺が共に Acc A.BとIXを壊す場合が面倒だが、このようなコードになる。
(右辺を計算する) PSHD (左辺のアドレスを計算してIXに入れる) PULD STD off,X
左辺から計算するとこうなる。
(左辺のアドレスを計算して、Acc A,Bに入れる) PSHD (右辺を計算して、Acc A,Bに入れる) PULX STD off,X
6809ならPULSXは簡単だが、6800だと次のような長いコードになる。18cycles,5bytes。遅い。
TSX LDX 0,X INS INS
結局何が必要か?
コード生成の前に、部分式を評価する場合に破壊されるレジスタ、結果が得られるレジスタを知っておく必要がある。dereferenceが絡むと面倒くさい。
続く
ディスカッション
コメント一覧
まだ、コメントがありません