An Incremental Approach to Compiler Construction

aloha さんのやや昔の記事から。
http://alohakun.blog7.fc2.com/blog-entry-482.html
孫引きだけど。
http://www.cs.indiana.edu/~aghuloum/
を参照し、gcc -S の力を借りると、難しいとされるコンパイラの重要な部分に集中して試すことができる、はず。

手元に x86 マシンが無く、PPCMac しかない。しかし、gcc(gcc version 4.0.1 (Apple Computer, Inc))は手元にあるので、

と、

を読めば、同じようにコンパイラが実装できる、はず。

int scheme_entry () {
  return 7;
}

上記を gcc --omit-frame-pointer -S ctest.c でコンパイルすると、

	.section __TEXT,__text,regular,pure_instructions
	.section __TEXT,__picsymbolstub1,symbol_stubs,pure_instructions,32
	.machine ppc
	.text
	.align 2
	.globl _scheme_entry
_scheme_entry:
	li r0,7
	mr r3,r0
	blr
	.subsections_via_symbols

こんな出力(ctest.s)が得られる。確かに微妙に違うけど、

  • li は簡略ニーモニックとよばれるもの。 li r0, 7 === addi r0,0,7 で、この場合 7 を r0 に格納する。
  • mr も簡略ニーモニック、ムーブ・レジスタ。mr r3,r0 は r0 の内容を r3 にコピーする。
  • blr は無条件分岐。

ここで rD は汎用レジスタ。32個ある、はず。なんでいきなり r3 が出てきたかはまだ分からない。

最適化オプションを変えると少し分かりやすくなった。ついでに呼び出す方も追加してみた。

int scheme_entry () {
  return 7;
}

int foo () {
  return 3 + scheme_entry();
}

上記を gcc -O3 -fno-inline-functions --omit-frame-pointer -S ctest.c でコンパイルした。

	.globl _scheme_entry
_scheme_entry:
	li r3,7
	blr
	.align 2
	.p2align 4,,15
	.globl _foo
_foo:
	mflr r0
	stw r0,8(r1)
	stwu r1,-80(r1)
	bl _scheme_entry
	addi r1,r1,80
	addi r3,r3,3
	lwz r0,8(r1)
	mtlr r0
	blr
	.subsections_via_symbols

"-fno-inline-functions" を付けないと、 foo は(ご丁寧にも) return 10; と同等に最適化されてしまい、知りたい内容が無くなってしまう。

  • scheme_entry はこんどはいきなり r3 に 7 をセットしている。
  • foo は、なんか思ったより随分とややこしい。
  • LR とはリンク・レジスタ。サブルーチンからの復帰のアドレスを保持するのに使われる、とある。
  • mflr r0 は LR の内容を r0 にコピーする。
  • stw は Store Word。stw r0,8(r1) で、 r0 の内容が実効アドレスEA=(r1|0)+8 の指定するメモリのワードにストアされる。??
  • stwu は Store Word with Update。これも分からない。80 は何のための数字だろう。
  • addi r1,r1,80 は r1 に 80 を足したものを r1 に格納。
  • addi r3,r3,3 は r3 に3 を足したものを r3 に格納。
  • lwz は load word and zero。
  • mtlr r0 は mtspr 8,r0 と同じ意味で、これは r0 の内容を LR に格納する、の意味。

何か値を退避させたりしている、とは想像できるが、詳細がまるで不明。

http://www.ibm.com/developerworks/jp/linux/library/l-ppc/index.html を見て少し分かった。
以下引用。

  • PowerPCにはGPRがたくさんあるので (ia32の8個に対して32個)、引数は、gpr3 以下のレジスターに入れて渡す。
  • レジスターgpr3 からgpr12 までは、揮発性の (呼び出し側で保証) レジスターとし、これらのレジスターは、(必要な場合) サブルーチンを呼び出す前に保存し、リターンした後に復旧しなければならない。
  • レジスターgpr1 は、スタック・フレーム・ポインターとして使用する。

もの凄く常識なのだとおもうが、なるほど。いろいろ制限を付けたとしても(数値は255までとか)、コンパイラの道はこれでも険しいなぁ。


追記。Mac なので当然 H/W を売っている Apple に情報があるのだった。