読者です 読者をやめる 読者になる 読者になる

LLVMのお勉強2

LLVM

今日は、前回作成した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は書き方が少し特殊らしい

今日はこれでお終い。