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, world
clang.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 などで実用性が認められたわけですが。)