オンライン勉強会 Online.sg #9「LLVM」

第9回テーマ 「LLVM」 - Online.sg


講師は@ujm
オリンピックまっただ中のバンクーバーからのUSTREAMでした。
現地では毎晩花火が上がるそうで、花火の音も聞こえてきました=D
ホントに時間と場所の壁は無くなったなぁと。

LLVMとは

The Low Level Virtual Machine (LLVM) is a compiler infrastructure, written in C++, which is designed for compile-time, link-time, run-time, and "idle-time" optimization of programs written in arbitrary programming languages. LLVM was originally developed as a research infrastructure at the University of Illinois at Urbana-Champaign to investigate dynamic compilation techniques for static and dynamic programming languages...

http://en.wikipedia.org/wiki/Low_Level_Virtual_Machine

LLVM を使うと、JavaJVMのように、言語に対して仮想機械を生成でき、その仮想機械が特定のマシンの機械語を生成するとともに、言語やプラットフォームとは独立した最適化を行う。

http://ja.wikipedia.org/wiki/Low_Level_Virtual_Machine

## LLVMの用途と目的

コンパイラを作る人のための道具。

* 新しいコンパイル型言語を作るなら、LLVMアセンブリ言語にさえ変換すればOK (C言語経由でもOK)
* LLVMならばMac, Linux, Windowsで確実に動く上に、かなり速い。


## 問題点

* まだ混沌 (最適化の二度漬けなど)
* LLVMを使った処理系をビルドするのが難しいときも (MacRuby)
* 変化がすごすぎる (終わらない`svn up`)

http://github.com/ujihisa/onsg9/blob/master/slides.md

演習

Hello Worldを出力するサンプルコード helloworld.ll に対して、

  1. LLVMアセンブリ言語ファイル(.ll) から LLVMビットコード(.bc)に変換
  2. LLVMビットコード(.bc)を最適化し、別のLLVMビットコード(.bc)を生成
  3. LLVMビットコード(.bc) から LLVMアセンブリ言語ファイル(.ll) に変換

を実行して元のコードと比べる

gitからサンプルコードをコピーする
$ git clone git://github.com/ujihisa/onsg9
$ cd onsg9

LLVMアセンブリ言語ファイル(.ll) から LLVMビットコード(.bc)に変換
$ llvm-as a.ll

LLVMビットコード(.bc)を最適化し、別のLLVMビットコード(.bc)を生成(1回目)
$ opt -O3 a.bc -o a2.bc

LLVMビットコード(.bc)を最適化し、別のLLVMビットコード(.bc)を生成(2回目)
$ opt -O3 a2.bc -o a3.bc

LLVMビットコード(.bc) から LLVMアセンブリ言語ファイル(.ll) に変換
$ llvm-dis a3.bc -o a3.ll

行数を出力
$ wc -l *.ll
  2714 a.ll
  25 a3.ll
$ cat a3.ll
; ModuleID = 'a3.bc'

define void @main() nounwind {
init:
  %h = alloca [1024 x i8], align 1                ; <[1024 x i8]*> [#uses=1]
  %initialize0 = getelementptr [1024 x i8]* %h, i64 0, i64 0 ; <i8*> [#uses=1]
  call void @llvm.memset.i64(i8* %initialize0, i8 0, i64 1024, i32 1)
  %0 = call i32 @putchar(i8 72)                   ; <i32> [#uses=0]
  %1 = call i32 @putchar(i8 101)                  ; <i32> [#uses=0]
  %2 = call i32 @putchar(i8 108)                  ; <i32> [#uses=0]
  %3 = call i32 @putchar(i8 108)                  ; <i32> [#uses=0]
  %4 = call i32 @putchar(i8 111)                  ; <i32> [#uses=0]
  %5 = call i32 @putchar(i8 32)                   ; <i32> [#uses=0]
  %6 = call i32 @putchar(i8 87)                   ; <i32> [#uses=0]
  %7 = call i32 @putchar(i8 111)                  ; <i32> [#uses=0]
  %8 = call i32 @putchar(i8 114)                  ; <i32> [#uses=0]
  %9 = call i32 @putchar(i8 108)                  ; <i32> [#uses=0]
  %10 = call i32 @putchar(i8 100)                 ; <i32> [#uses=0]
  %11 = call i32 @putchar(i8 33)                  ; <i32> [#uses=0]
  ret void
}

declare i32 @putchar(i8) nounwind

declare void @llvm.memset.i64(i8* nocapture, i8, i64, i32) nounwind

2714行が25行に!

コードを見比べると「Hello World!」を出力するためにポインタを1つ1つインクリメントしてたものが直接キャラクタを出力するようになった感じですね。