Amsterdam Compiler Kit を6800向けに改造する (4) 関数引数がcharのとき

BASICMASTER, 昔のパソコン

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

続く。

リンク