2010年03月11日
Clang と LLVM を使ってみる。
前回は Clang フロントエンドだけをビルドしました。
今回は LLVM もビルドして、実際に使ってみます。とはいっても、前回と同様に LLVM.sln を開いて、ALL_BUILD するだけです。時間がかかるかなと思って前回は試さなかったのですが、10 分もかかりませんでした。
lli.exe (LLVM 仮想マシン本体。JVM の java コマンドのようなもの)、llc.exe (ネイティブアセンブリコードへのトランスレータ) の他に、llvm-as.exe や llvm-ld.exe、llvm-nm.exe など、おなじみの binutils ライクなコマンド群が生成されます。
今回は LLVM もビルドして、実際に使ってみます。とはいっても、前回と同様に LLVM.sln を開いて、ALL_BUILD するだけです。時間がかかるかなと思って前回は試さなかったのですが、10 分もかかりませんでした。
lli.exe (LLVM 仮想マシン本体。JVM の java コマンドのようなもの)、llc.exe (ネイティブアセンブリコードへのトランスレータ) の他に、llvm-as.exe や llvm-ld.exe、llvm-nm.exe など、おなじみの binutils ライクなコマンド群が生成されます。
まず、GCC にパスが通っていれば、Clang はデフォルトで gcc をアセンブラとして使用するので、通常の実行形式が作れます。
(PATH の位置などは、環境に応じて適宜読み替えてください。)
ネイティブコード生成は、その時点で情報が欠落し、それ以上の最適化を阻害するため、LLVM の本来の目標 Multi-Stage optimization からすると邪道で、とりあえず GCC の置き換え、C/C++/Obj-C コンパイラとして使用するための、ある意味ではオマケのようなものだと言えます。(ただし、そのおかげでいち早く FreeBSD などで実用性が認められたわけですが。)
(PATH の位置などは、環境に応じて適宜読み替えてください。)
C:\llvm\work>PATH %PATH%;C:\llvm\bin\debug;C:\MinGW\bin C:\llvm\work>cat t.c #include <stdio.h> int main(int argc, char *argv[]) { printf("hello, world\n"); return 0; } C:\llvm\work>clang t.c C:\llvm\work>ls a.out t.c C:\llvm\work>a.out hello, worldclang.exe で LLVM アセンブリコードを生成し、llvm-as.exe でアセンブルし、lli.exe で実行することもできます。
C:\llvm\work>clang -emit-llvm -S t.c C:\llvm\work>ls t.c t.s C:\llvm\work>llvm-as t.s C:\llvm\work>ls t.c t.s t.s.bc C:\llvm\work>lli t.s.bc hello, worldどうも設定が悪いのか、直接ビットコードを生成することはできない感じです。そのため、-S を経由しているのですが、拡張子が重複してしまい t.s.bc とか変なことになってます。
C:\llvm\work>clang -emit-llvm t.c clang: error: 'i686-pc-win32': unable to pass LLVM bit-code files to linkerbc ファイルは、BitCode のことで、Java のクラスファイル(というと語弊がありますが。LLVM には、JVM のよりも Low-Level な、機械語に近い構造しか存在しませんので。)のようなものです。 bc ファイルを逆アセンブルすると ll ファイルが生成されますが、これは clang -S で生成されるものと全く同じ内容です。(ファイル名などは当然異なりますが。)
C:\llvm\work>llvm-dis t.s.bc C:\llvm\work>ls t.c t.s t.s.bc t.s.ll C:\llvm\work>diff t.s t.s.ll 1c1 < ; ModuleID = 't.c' --- > ; ModuleID = 't.s.bc'この ll ファイルなり、s ファイルなりは、LLVM のアセンブリコードなわけですが、これをネイティブ環境のアセンブリコードに変換するには、llc.exe コマンドを使用します。
C:\llvm\work>llc t.s.ll C:\llvm\work>ls t.c t.s t.s.bc t.s.ll t.s.s C:\llvm\work>cat t.s.s .def _main; .scl 2; .type 32; .endef .text .globl _main .align 16, 0x90 _main: # @main # BB#0: # %entry subl $20, %esp movl $0, 16(%esp) movl 24(%esp), %eax movl %eax, 12(%esp) movl 28(%esp), %eax movl %eax, 8(%esp) movl $L_.str, (%esp) call _printf movl $0, 16(%esp) xorl %eax, %eax addl $20, %esp ret .data L_.str: # @.str .asciz "hello, world\n"これは当然、gcc でアセンブル・リンクして実行できます。
C:\llvm\work>gcc t.s.s C:\llvm\work>ls a.exe t.c t.s t.s.bc t.s.ll t.s.s C:\llvm\work>a.exe hello, worldいろいろややこしい構造になっていますが、これは LLVM が、仮想マシンの上でプロファイルを取得しながらの、動的な最適化を行うためのプラットフォームだからです。
ネイティブコード生成は、その時点で情報が欠落し、それ以上の最適化を阻害するため、LLVM の本来の目標 Multi-Stage optimization からすると邪道で、とりあえず GCC の置き換え、C/C++/Obj-C コンパイラとして使用するための、ある意味ではオマケのようなものだと言えます。(ただし、そのおかげでいち早く FreeBSD などで実用性が認められたわけですが。)