Tiny Basicの代入文「A=A+10」のコード生成で、
(1)変数Aを定義、ゼロで初期化
(2)変数Aのメモリをレジスタ1に取り出す
(3)定数10をレジスタ2に入れる
(4)レジスタ1とレジスタ2を加算し、結果をレジスタ1に入れる
(5)レジスタ1の内容を変数Aのメモリに書き込む
というのを、実際にLLVM-IR中間コードアセンブラで書いてみる。
まず、
(1)変数Aを定義、ゼロで初期化
について、
変数Aを定義するにはLLVM-IRアセンブリ言語では、
%A = alloca i32 , align 4
と書く。
allocaというのはC言語などでお馴染みのメモリを割り当てる命令だ。
i32というのは32bit整数型という意味だ。(作っているのが整数型Tiny Basicなので)
%Aは、そのメモリのアドレス値を記録するポインタ変数になる。
メモリを割り当てただけでは、その中身がゼロにならないので、自前で初期化をしないといけない。
store i32 0 , i32* %A , align 4
と、store命令を使って変数Aのメモリに0を書き込む。
次に、
(2)変数Aのメモリをレジスタ1に取り出す
について、
%1 = load i32 * %A , align 4
と書く。
%1というのは使い捨てのレジスタ的な変数だ。
次に、
(3)定数10をレジスタ2に入れる
について、
%2 = 10
と書きたいのだけど、直接書けない。
あらかじめ変数accを定義して、
%acc = alloca i32 , align 4
そのaccを使って、
store i32 10 , i32* %acc , align 4
%2 = load i32 * %acc , align 4
と書いた。
もっとスマートな書き方があるかもしれない。
次に、
(4)レジスタ1とレジスタ2を加算し、結果をレジスタ1に入れる
について、
%3 = add nsw i32 %1, %2
と書く。
本当は%1に結果を入れたいのだけど、それはできない。
前に書いた静的単一代入形式(SAA)のルールだからだ。
代わりに新しい 使い捨てのレジスタ的な変数%3を使う。
次に、
(5)レジスタ1の内容を変数Aのメモリに書き込む
について、
レジスタは3に変更。
store i32 %3 , i32* %A , align 4
と書く。
store命令で、変数Aのメモリに%3の内容を書き込む。
このように手作業でコード生成すると、「A=A+10」はこんな感じのLLVM-IRのコードになる。
%acc = alloca i32 , align 4
%A = alloca i32 , align 4
store i32 0 , i32* %A , align 4
%1 = load i32 * %A , align 4
store i32 10 , i32* %acc , align 4
%2 = load i32 * %acc , align 4
%3 = add nsw i32 %1, %2
store i32 %3 , i32* %A , align 4
変数の番号の%1,%2,%3とかの部分を決めうちでなく汎用性を持たせてコード生成するプログラムを作れば、自動でコード生成するコンパイラとなる。