Amsterdam Compiler Kit を6800向けに改造する (4) 関数引数がcharのとき
Amsterdam Compiler Kit を改造してMC6800用のコードを出力する試みの4回目。今回は関数引数がcharのときの処理
EM codeはワードマシン
ack は、コンパイル結果を EM codeという中間コードに落とす。 EMはオペコード1バイトの可変長命令を使っていて、レジスタサイズは2または4バイトとなっている。
関数を呼び出す時は、char であっても1ワードをpushして呼び出す(ここは、最適化できるのだろうか。まだわからない)
charであっても2バイトがスタックに積まれてくるので、呼ばれる側(callee)は、2バイト単位で引数アドレスを計算する。
little endian と big endian のマシンでは、引数のアドレスがズレるはずだが、EM codeは little/big で共通なのでアドレス計算も共通である。
ざっと見た感じでは、little endianに合ったコードになっていて、charであってもワード先頭のアドレスを計算しているようだ。
x86 とか Z80 とか 6502 とか PDP-11 だとこれで問題はない。現代となっては少数派となってしまった Big Endian CPUだけが困るわけだ。
MC6800はBig endianのマシンなので1バイトずれる。バグる。
他のCPUではどうなっているのか
わからなかったので、issue を書いてから気がついた。ackにはMC68000とpowerPC用のコードジェネレータがあるので、これを見ればいい。
すると、6502/Z80では出力されていない謎のコードが見つかった。なんだこれは (詳細は下記のissueに書いてあります)。
よくよく調べてみると、次のような仕組みになっていた。
コンパイラは関数の冒頭で、調整が必要な引数(char)があると、それを補正するコードを出力する。
まず、関数引数をワード単位で読み込んで、得られた値を同じアドレスにバイト単位として書き込む。
Big Endianのマシンだとcharとして正しい位置に書き込まれる。
Little Endianのマシンでは、何もしないコードなので optimizeして消してしまえばよい。
一度補正してしまえば関数本体では、LittleもBigも同様に処理すれば良い。
巧妙だがいささかトリッキーだし、Big EndianのCPUではchar引数の数だけオーバーヘッドになる。MC6800だと1つにつき15cycleのオーバーヘッドだ。バカにならない。
ack 6800の現状
整数版のマンデルブロートが動くようになった。
emu6800 -d 6800 emu6800.img /dev/null CPU cycles = 210 111111111111111111222222222333334568BC67443322222211111111111000000000000000000 11111111111111111122222222233344598C 77943333222221111111110000000000000000000 111111111111112222222233324444556 955433332211111111100000000000000000000 111111111211112222222333455665778 976554444222211111100000000000000000000 11111112222222233333334457 AB9 787B5432111111111100000000000000000 11112222222222333333444667 532222111111111000000000000000 11111222333444444444555A 96443322211111110000000000000000 12222223345D6657 6555679 AA43322211111110000000000000000 222233334569 8C E8789 B43322111111110000000000000000 223333345578D E 43322211111111111100000000000 3344444789A 543322111111111111100000000000 5555658A C6433222222111111111100000000000 975443322221111111111100000000000 5555658A C6433222222111111111100000000000 3344444789A 543322111111111111100000000000 223333345578D E 43322211111111111100000000000 222233334569 8C E8789 B43322111111110000000000000000 12222223345D6657 6555679 AA43322211111110000000000000000 11111222333444444444555A 96443322211111110000000000000000 11112222222222333333444667 532222111111111000000000000000 11111112222222233333334457 AB9 787B5432111111111100000000000000000 111111111211112222222333455665778 976554444222211111100000000000000000000 111111111111112222222233324444556 955433332211111111100000000000000000000 11111111111111111122222222233344598C 77943333222221111111110000000000000000000 111111111111111111222222222333334568BC67443322222211111111111000000000000000000 CPU cycles = 114554407
MC6800用のC compiler同士で比較してみた。ほぼ乗除算ルーチンの速度差である。chibicc-6800は分岐が早いので良い数値が出ている。
Compiler | Cycles |
---|---|
acl | 114,554,197 |
CC68 | 133,893,286 |
Fuzix CC | 146,818,028 |
chibicc-6800 -O2 | 90,918,135 |
続く。
ディスカッション
コメント一覧
まだ、コメントがありません