mcommit's message

ソフトウェア開発の仕事をしているsimotinといいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

Cコンパイラ作成入門をarmで試してみる

久しぶりに記事を書きます。

このところ数学とか電気回路の勉強ばかりで、仕事以外でコードを書いていないので気分転換にコードを書きたい欲が強くなっていたのですが、@rui314さんがCコンパイラ作成入門という面白そうな本を書かれているそうなので自分もC言語でのコンパイラ開発試してみたいと思います。

低レイヤを知りたい人のための Cコンパイラ作成入門

raspberrypiに移植してみる

このコンパイラ作成入門の本は、任意のexitコードを返すアセンブラを出力するところから始めるようですが、環境としてx86_64/linuxもしくはx86/linuxを想定されています。
私は普段windowsを使っていて、*nix系のコードはcygwin上で書いているのでそのままではコンパイルが出来ません。
どうしたものかと思ったのですが、手元でいつでも気軽に動かせるlinuxというとraspberrypiになるのでarmの勉強も兼ねてこの本のコードをraspberrypiに移植するような形で遊んでみたいと思います。

任意のコードでのreturn

コンパイラ本体の作成は、コマンドライン引数で受け取った数値でreturn(exit)するアセンブラを出力するコードから始まるのですが、とりあえずそれっぽいコードを書いてみました。

main.c

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	FILE *fp = stdout;
	
	if (argc < 2) {
		fprintf(stderr, "input code\n");
		return -1;
	}

	fprintf(fp, ".text\n");
	fprintf(fp, ".global main\n");
	fprintf(fp, "main:\n");
	fprintf(fp, "  mov r0, #%d\n", atoi(argv[1]));
	fprintf(fp, "  bx lr\n");
	return 0;
}


$ ./a.out 39 > tmp.s
$ gcc -o tmp tmp.s
$ ./tmp
$ echo $?
39

うむ。受け取った引数でexitできてそう。

armのアセンブラについて

戻り値に使うレジスタ

armでは戻り値に使うレジスタは r0 レジスタを使っている。

return命令

armでは関数の呼び出し元に戻る命令はないようで分岐命令で戻るみたいだ。なのでスタックの操作は自分でしておく必要がある。bx は命令セットの切替(thumb/arm)を伴う無条件分岐命令らしい。lrはr14レジスタエイリアスでリンクレジスタのこと。関数呼び出しの際に戻り先のアドレスをのlrレジスタに退避しておく。

移植してみたコードでは、コマンドライン引数の値をr0レジスタに入れて呼び出し元に分岐している。armのABIはよく分かっていないけどとりあえず動いたのでよしとしよう。

感想

とりあえず触りのところを遊んでみましたが、まずはmain関数のアセンブラを吐いてみるっていうのはなかなか斬新だと思いました。linux/gccだとリンカがよきに計らってくれるのでこれはいい!

armのアセンブラよく知らないけどx86_64/x86と比べるとどうなんだろう。RISCということで出力するアセンブラの量は多そうですが、なんとなくエンコーディングとかは直感的な気がします。
ということで、バックエンドまでいくと実はarmのほうが楽チン!という落ちが待っていることを期待つつぼちぼち遊んでみたいです。