LLVMのお勉強2
今日は、前回作成したHelloWorld.llの中身を見ていく。 HelloWorld.llはこんなだった。
; ModuleID = 'HelloWorld.c' target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" @.str = private unnamed_addr constant [12 x i8] c"HelloWorld\0A\00", align 1 ; Function Attrs: nounwind uwtable define i32 @main() #0 { %1 = alloca i32, align 4 store i32 0, i32* %1, align 4 %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i32 0, i32 0)) ret i32 0 } declare i32 @printf(i8*, ...) #1 attributes #0 = { nounwind uwtable "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #1 = { "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.ident = !{!0} !0 = !{!"clang version 3.8.1-12ubuntu1 (tags/RELEASE_381/final)"}
ここLLVM Language Reference Manual — LLVM 5 documentationを参考にした。
; ModuleID = 'HelloWorld.c' ; で始まる行はコメント文。記述からして、ソースファイル名
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" メモリ上でデータがどういう風に配置されるかを規定する。-区切りで記述していく。 e リトルエンディアン m:<mangling> パラメータの指定によって、llvm namesがmangledされる。mangledの意味がいまいち分からない。 m:e ELFmangling/プライベートシンボルは、.Lが接頭辞につく i<size>:<abi>:<pref> サイズ<size>の整数型のabiインターフェース<abi>,アライメント<pref>を規定する ここでは、abiインターフェースのbitアライメントを指定している? f<size>:<abi>:<pref> サイズ<size>の浮動小数点型とabiインターフェース<abi>,アライメント<pref>を規定する sizeはfloat(32),double(64)、80とか128(long double)の場合もある n<size1>:<size2>:<size3>... cpuがサポートする整数型のサイズ。n8:16:32:64はX86-64アーキテクチャ S<size> スタックのアライメントを規定する。8の倍数に限る 指定されなかったサイズの型については、デフォルトが適用される
abiってなにさ。
調べてみると、ApplicationBinaryInterfaceの略のようだ。
target triple = "x86_64-pc-linux-gnu" ターゲットのアーキテクチャの情報 ARCHITECTURE-VENDOR-OPERATING_SYSTEM-ENVIRONMENT
@.str = private unnamed_addr constant [12 x i8] c"HelloWorld\0A\00", align 1 グローバル変数は@.***で定義される。ほぼ、読んだままのことが書いてあるっぽい。
; Function Attrs: nounwind uwtable define i32 @main() #0 { defineで関数定義。i32は返り値の型。@mainが関数名,()の中に何もないので、引数をとらない。
%1 = alloca i32, align 4 %数字は無名変数扱い。alloca命令は、関数終了時に自動で開放されるスタックを確保する
store i32 0, i32* %1, align 4 store a b でaの値をbに保存する
%2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i32 0, i32 0)) call命令は関数呼び出しを示す。@printfだから、printf関数を呼び出している
ret i32 0 return。0を返している }
declare i32 @printf(i8*, ...) #1 declare文は関数宣言。printfをよそから読み込んでいるから(定義は別にあるから)declareでやっている?
attributes #0 = { nounwind uwtable "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #1 = { "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } 引数属性をグループ(#0,#1)ごとに定義。 関数名の後ろについてた#0とかは、これを指している。別に排他ではなく、#0と#1を同時に指定することもできる
!llvm.ident = !{!0} !0 = !{!"clang version 3.8.1-12ubuntu1 (tags/RELEASE_381/final)"} !で始まるのはmetadata型。metadataは書き方が少し特殊らしい
今日はこれでお終い。
LLVMのお勉強1
資料
きつねさんでもわかるLLVM-1.0.0
環境
OS:Lubuntu clang:version 3.8.1
勉強内容
資料を参考に、CファイルをLLVM-IRへ
#include <stdio.h> int main (){ printf("HelloWorld\n"); return 0; }
コマンドは次のを使った。
clang -emit-llvm -S -o HelloWorld.ll HelloWorld.c
…何やってるかわからないので、調べる。
man clang
を叩く。
-o ***: 出力ファイルを***に指定 -S: LLVMアセンブリファイルの生成 -emit-llvm: -Sオプションと一緒に指定すると、出力ファイルがLLVM中間言語アセンブリになる
というわけで、LLVM中間言語アセンブリを出力していることが分かった。
で、出力された内容(HelloWorld.ll)が次。
; ModuleID = 'HelloWorld.c' target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" @.str = private unnamed_addr constant [12 x i8] c"HelloWorld\0A\00", align 1 ; Function Attrs: nounwind uwtable define i32 @main() #0 { %1 = alloca i32, align 4 store i32 0, i32* %1, align 4 %2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str, i32 0, i32 0)) ret i32 0 } declare i32 @printf(i8*, ...) #1 attributes #0 = { nounwind uwtable "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } attributes #1 = { "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-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" } !llvm.ident = !{!0} !0 = !{!"clang version 3.8.1-12ubuntu1 (tags/RELEASE_381/final)"}
あれ、資料にのってるのと違う…
資料では、clang3.2系をベースに解説しているので、多少違いがあっても仕方ない。
次回は、HelloWorld.llの中身を紐解いていこうと思う。