Amsterdam Compiler Kit を6800向けに改造する (2) cg (code generator)

BASICMASTER, 昔のパソコン

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] | |

続く


リンク