LLVM IR、LLVM bitcodeを扱うコマンドたちのメモ

LLVM IR、LLVM bitcodeを扱うコマンドをよく忘れるのでメモしておく。 CのコードをLLVM IR、LLVM bitcodeに変換するコマンド、LLVM bitcodeをインタプリタから実行するコマンド、コンパイルするコマンドなどを書いておく。 以下のCのコードを変換していく。

# include <stdio.h>

int main() { 
  int a = 1;
  int b = 2;
  int sum = a + b;
  printf("%d", sum);
  return 0;
}

LLVM IRを出力する

LLVM内ではLLVM IRという中間言語表現が用いられる。LLVM内で使えるアセンブリのようなものである。 LLVMを用いたコンパイラでは、ソースコードLLVM IRコードに変換したあと、そのLLVM IRコードをターゲットのアーキテクチャのバイナリに変換することでコンパイルが行われる。

Cのコードを変換する

clangに-emit-llvm-Sの2つのオプションを指定して実行するとLLVM IRが出力される。 以下のようにコマンドを実行するとtest.llが出力される。

$ clang test.c -emit-llvm -S

出力されたLLVM IR

以下のようなLLVM IRのコードが出力される。

$ cat test.ll
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.12.0"

@.str = private unnamed_addr constant [3 x i8] c"%d\00", align 1

; Function Attrs: nounwind ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 1, i32* %2, align 4
  store i32 2, i32* %3, align 4
  %5 = load i32, i32* %2, align 4
  %6 = load i32, i32* %3, align 4
  %7 = add nsw i32 %5, %6
  store i32 %7, i32* %4, align 4
  %8 = load i32, i32* %4, align 4
  %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str, i32 0, i32 0), i32 %8)
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { nounwind ssp 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"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "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"="penryn" "target-features"="+cx16,+fxsr,+mmx,+sse,+sse2,+sse3,+sse4.1,+ssse3" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0}
!llvm.ident = !{!1}

!0 = !{i32 1, !"PIC Level", i32 2}
!1 = !{!"Apple LLVM version 8.0.0 (clang-800.0.38)"}

LLVM bitcodeを出力する

LLVM bitcodeはLLVM IRのバイナリフォーマットである。 LLVM IRと LLVM bitocodeは相互変換でき、LLVM bitcodeはバイナリ(.out, .exeなど)に変換せずとも、LLVMインタプリタから直接実行することができる。

Cのコードを変換する

clangに-emit-llvm-cの2つのオプションを指定して実行するとLLVM bitcodeが出力される。 以下のようにコマンドを実行するとtest.bcが出力される。

$ clang test.c -emit-llvm -c

LLVM IRを変換する

llvm-asコマンドを使うことでLLVM IRをLLVM bitcodeに変換できる。 以下のようにコマンドを実行するとtest.bcが出力される。

$ llvm-as test.ll 

出力されたLLVM bitcode

人の目で直接読むのはきびしい。

$ file test.bc
test.bc: LLVM bitcode, wrapper x86_64
$ hexdump -C test.bc
00000000  de c0 17 0b 00 00 00 00  14 00 00 00 a8 09 00 00  |................|
00000010  07 00 00 01 42 43 c0 de  35 14 00 00 05 00 00 00  |....BC..5.......|
00000020  62 0c 30 24 49 59 be a6  ee d3 3e 2d 44 01 32 05  |b.0$IY....>-D.2.|
00000030  00 00 00 00 21 0c 00 00  1e 02 00 00 0b 02 21 00  |....!.........!.|
00000040  02 00 00 00 13 00 00 00  07 81 23 91 41 c8 04 49  |..........#.A..I|
00000050  06 10 32 39 92 01 84 0c  25 05 08 19 1e 04 8b 62  |..29....%......b|
00000060  80 10 45 02 42 92 0b 42  84 10 32 14 38 08 18 4b  |..E.B..B..2.8..K|
00000070  0a 32 42 88 48 90 14 20  43 46 88 a5 00 19 32 42  |.2B.H.. CF....2B|
00000080  04 49 0e 90 11 22 c4 50  41 51 81 8c e1 83 e5 8a  |.I...".PAQ......|
00000090  04 21 46 06 51 18 00 00  e9 00 00 00 1b 4c 25 f8  |.!F.Q........L%.|
000000a0  ff ff ff ff 01 90 00 0d  08 03 82 1c d2 61 1e c2  |.............a..|
000000b0  41 1c d8 a1 1c da 80 1e  c2 21 1d d8 a1 0d c6 21  |A........!.....!|
....

LLVM bitcodeをLLVM IRにデコンパイル

相互変換できるのでLLVM IRに戻すことができる。すごい。

$ llvm-dis test.bc

LLVM bitcodeをインタプリタで実行する

lliコマンドを使うことでLLVM bitcodeをインタプリタから直接実行できる。

$ lli test.bc 
3

LLVMの bitcodeをバイナリにする

llcコマンドを使うことでLLVM bitcodeをターゲットアーキテクチャアセンブリに変換し、そのアセンブリをclangでコンパイルすることでバイナリが出力される。

$ llc test.bc 
$ clang test.s
$ ./a.out
3