とりあえずの学習メモとして残す。 理解が怪しいところは?や(?)などをつけている。 理解が進んだ後に整理したものを書きたい。
int main (void) { return 0; }
このプログラムはCにおける(多分)最小のプログラムで、単にステータスコード0を返すだけのプログラム。
これをclang -S -emit-llvm
でLLVM IRに変換するとこうなった。(macOSで吐いた)
; ModuleID = 'hello.c' source_filename = "hello.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" ; Function Attrs: noinline nounwind ssp uwtable define i32 @main() #0 { %1 = alloca i32, align 4 store i32 0, i32* %1, align 4 ret i32 0 } attributes #0 = { noinline nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"PIC Level", i32 2} !1 = !{!"clang version 4.0.0 (tags/RELEASE_400/final)"}
まず、';‘から始まる行はコメントなのでプログラムとしては無視する。
source_filename = "hello.c"
は見ての通り元のソースファイルの名前で、プロファイル時にユニークなローカル関数の識別子を生成するために使っているらしい。
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
はメモリ上のデータレイアウトを記述している。
- ‘-'が区切り文字。
- ‘e'はリトルエンディアン。
- ’m:o'は名前修飾の方法を指定している。'o'なので'Mach-O'式の名前修飾を行う。
- ‘i64:64'は64bit整数のアライメントを64に指定している。この場合は64の倍数?
- ‘f80:128'も浮動小数点数であること以外は上に同様
- ‘n8:16:32:64'はありうる整数のbitを指定している。これはx86-64の場合。
- ‘S128'はスタックのアライメント。
define i32 @main() #0 { %1 = alloca i32, align 4 store i32 0, i32* %1, align 4 ret i32 0 }
今回のプログラムの本体がここ。 LLVM IRはこのようにC+アセンブラっぽい文法で書かれている。
‘i32'は32bit整数。LLVMのドキュメントに様々な型について詳細な情報がある。Rustっぽい。
‘%1'のように’%‘で始まるものがローカル識別子。 この場合、%1はアライメント4, 32bit整数のレジスタとして定義されている。
‘store i32 0, i32* %1, align 4'で、%1に0を代入している。alignは%1のものと一致するように指定される(?)。
‘ret i32 0'はC言語の'return 0;'に対応する。
%1の意味は…
最適化のせいかと考えたが、-O0でも結果は変わらず。あとで調べる。
(2017 6/20追記)
よく考えたら-Sだと-O0の結果が出力されてる。
clang -emit-llvm -S -01
で出力させると削除される。
define i32 @main() local_unnamed_addr #0 { ret i32 0 }
(追記ここまで)
attributes #0 = { noinline nounwind ssp uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
この一行は#0という名前のattribute groupを宣言している。先程のmain() #0
の'#0'の部分へ代入されるっぽい。
#0
と#1
という2つのattribute groupがあるとき、#0 #1
と書くと2つが接続される。
!llvm.module.flags = !{!0} !llvm.ident = !{!1} !0 = !{i32 1, !"PIC Level", i32 2} !1 = !{!"clang version 4.0.0 (tags/RELEASE_400/final)"}
ここではモジュール全体のメタデータを定義している。
もう少し複雑なプログラムについてはまた今度。