Amsterdam Compiler Kit を6800向けに改造する (2) cg (code generator)
Amsterdam Compiler Kit を改造してMC6800用のコードを出力する試みの2回目。
cg (code generator)
ソースプログラムは仮想マシン(スタックマシン)EMのコードにコンパイルされ、その後、cg あるいは ncg によりCPUに応じたコードに変換される。ncg は new code generatorだろうか。
cg はテーブル参照で機械的にコードを置き換えるので、こちらを先に作成してみよう。mach/6500/ や mach/z80/ に、6502やz80用のcgがあるのでそれを参考にしてみた。
作成したテーブルは、 ack-6800/mach/mc6800/cg/table at default · zu2/ack-6800 に置いた。
cg の先頭部分は table から参照されるマクロである。em_bsize は前回解説したスタック上のギャップである(Amsterdam Compiler Kit を6800向けに改造する (1) スタック構造)。
今回はem_bsizeは2としてある(LBポインタと戻りアドレスだけがスタックに積まれる)。今後変更するかもしれない。
ND,D は(たぶん)引数が定義済みかどうかを表す。6502の定義をそのまま使っている。あとで考える。
BASEも前回説明したスタックフレームをアクセスするためのオフセット。LBl = LB-240 としておいて、オフセットを正にする。詳しくは前回の記事を見て欲しい。
MIN,MAX,IN はローカル変数・関数引数へのオフセットが -240から+10の範囲かをチェックするためのマクロ。この範囲であれば、ldx LBl / ldab off,x のようにしてアクセスできる。
#define em_bsize 2 /* must be equal to EM_BSIZE */ #define ND !defined($1) #define D defined($1) #define BASE 240 #define MIN (0-BASE) #define MAX (254-em_bsize-BASE) #define IN(x) (x>=MIN && x<=(MAX-2))
上記 #define に続いて、マクロ定義が続くが、まだ理解できていないのでパス。
テーブル部分は、EM中間コードの列、スタック状態、生成するコード、結果 が書かれている。
下記はEMコードlocを6800命令列に落とす部分のテーブル。最初のloc $1==0 は、定数0を読み込む。
次の loc ($1%256)==($1/256) は、たとえば 0x1212 のように上位バイトと下位バイトが同一の場合。
最後の loc は一般の場合である。6800コードの ! は注釈、#[$1].l や #[$1].h は、下位バイト・上位バイトを表わす。
allocate(R16)は、AccABレジスタペアを空けるように指示している。AccABにすでに値がある(スタックトップとして使われている)場合は、pshb/psha を行い、レジスタを空ける。
最後の %[a] は、結果がAccABペアに入ることを示す。6800では使えるレジスタペアが1つしかないので%[a]だけだが、Z80ではHL/DE/BCも使えるので、ここが変化する。
/* loc: load 16bit constant */ loc $1==0 | | allocate(R16) "\tclrb" "\tclra" | %[a] | | loc ($1%256)==($1/256) | | allocate(R16) "\tldab #[$1].l" "\ttba" | %[a] | | loc | | allocate(R16) "! loc" "\tldab #[$1].l" "\tldaa #[$1].h" | %[a] | |
EMコードはスタックマシンだが、スタックトップはレジスタに置くことができる(その方が効率が上がる)。
この工夫は、KUMAJIRIコンパイラの中間言語であるK-codeでも行われていた。広く使われていたテクニックなのだろう (初出はどこだろうか?)
I/O 1980年8月号 P.106-107 から引用
cg (code generator)の複合命令
EMコードを1命令ずつ変換したのでは、効率的なコードが生成できない。EMはスタックマシンなので、スタックに積んでは演算&POP、積んでは演算&POPの繰り返しになる。
そこで、cg/table では、EM命令の列をいっぺんにターゲットCPUのコードに変換できるようになっている。
下記は、loc lol adi の例。定数とローカル変数をスタックに積み、上位2つを足す操作だ。
lda命令で定数を読み、add命令でローカル変数と加算する。結果はAccAB(%[a])に置く。
/* GROUP 3 - INTEGER ARITHMETIC */ loc lol adi (IN($2) && $3==2) | | allocate(R16) "\tldx LBl" "\tldab #[$1].l" "\tldaa #[$1].h" "\taddb BASE+$2+1,x" "\tadca BASE+$2+0,x" | %[a] | |
続く
ディスカッション
コメント一覧
まだ、コメントがありません