【Ubuntu】LLVM/Clang を使ってみた

今の開発現場は未だにC言語のビルド環境がGCCですが,今回はLLVM/Clangを使ってみました。
また, clang-format を WEBサービス (go-clang-formatter)にしてみました。
シンプルなので実験として Golang で書いています。

LLVM/Clang 特徴

  • 最新の規格への対応が早い(C++11対応)
  • GCCとの互換性が高い
  • エラーメッセージが最小限かつ分かり易い
  • プラグインによる拡張
  • BSD License(厳密には異なる)

LLVM/Clang 構成

  • フロントエンド : Clangの機能。字句解析/構文解析(AST)を行い中間コード(LLVM IR)を生成する。
  • ミドルエンド : LLVM Coreの機能。LLVM IRを最適化して高速化・サイズを縮小化する。
  • バックエンド : CPUアーキテクチャに沿ったバイナリを生成する。

LLVM IRというインターフェイスを使っているが,粗結合のためコンパイラを作る場合に全てを作る必要がなくコストが小さいのが特徴です。

Install

OSXではXcode環境があればClangが使えます。

$ clang -v
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix

Ubuntu 14.04でインストールしてみます。stable版をパッケージマネージャでインストールします。

$ sudo apt-get install clang-3.4 clang-3.4-doc libclang-common-3.4-dev libclang-3.4-dev libclang1-3.4 libclang1-3.4-dbg libllvm-3.4-ocaml-dev libllvm3.4 libllvm3.4-dbg lldb-3.4 llvm-3.4 llvm-3.4-dev llvm-3.4-doc llvm-3.4-examples llvm-3.4-runtime clang-modernize-3.4 clang-format-3.4 python-clang-3.4 lldb-3.4-dev
$ clang -v
Ubuntu clang version 3.4-1ubuntu3 (tags/RELEASE_34/final) (based on LLVM 3.4)
Target: i386-pc-linux-gnu
Thread model: posix

以下はソースコードから最新版をビルドする場合です。
基本的にはビルド方法はここに書いてあります。

$ mkdir llvm3.4 && cd llvm3.4
$ svn co https://llvm.org/svn/llvm-project/llvm/trunk llvm
$ cd llvm/tools
$ svn co https://llvm.org/svn/llvm-project/cfe/trunk clang
$ cd ../llvm/projects
$ svn co https://llvm.org/svn/llvm-project/compiler-rt/trunk compiler-rt
$ mkdir ../llvm/build && cd ../llvm/build
$ ../llvm/configure
$ make

ビルドしてみます。

$ clang -o clang_example clang_example.c 

include-pathの確認。

$ clang  -print-search-dirs
programs: =/usr/bin:/usr/bin/../lib/gcc/i686-linux-gnu/4.9/../../../../i686-linux-gnu/bin
libraries: =/usr/bin/../lib/clang/3.4:/usr/bin/../lib/gcc/i686-linux-gnu/4.9:/usr/bin/../lib/gcc/i686-linux-gnu/4.9/../../../i386-linux-gnu:/lib/i386-linux-gnu:/usr/lib/i386-linux-gnu:/usr/bin/../lib/gcc/i686-linux-gnu/4.9/../../..:/lib:/usr/lib

また,clangが見に行くheaderの環境変数はCPATHです。

emit-astオプションはASTバイナリが生成されます。

$ clang -cc1  -print-decl-contexts  clang_example.c
clang_example.c:1:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
         ^
[translation unit] 0xab63ef4
         __builtin_va_list
        [function] myfunc(x)
             x
        [function] main()
             in
             out
         printf()
1 error generated.

* 捕捉 : XMLで吐き出すast-print-xml オプションは消えたようです。

続いて、周辺ツールを使ってみます。

clang-format

clang-formatはformatterです。整形前が以下。

#include <stdio.h>

int myfunc ( int x )
{
    return x * 2;
}

int main( void )
{
    int in = 9;
    int out = myfunc(in);

    printf("%d\n",out);

    return 0;
}

styleオプションはLLVM , Google , Chromium , Mozilla , WebKitがあります。
styleにLLVMを指定しました。

$ clang-format-3.4  -style=LLVM clang_format.c 

以下のように整形されます。

#include <stdio.h>

int myfunc(int x) { return x * 2; }

int main(void) {
  int in = 9;
  int out = myfunc(in);

  printf("%d\n", out);

  return 0;
}

デフォルトでは標準出力ですが,iオプションで上書きできます。

Clang Static Analyzer

静的解析ツールがついています。XCodeの静的解析はこの機能を使っています。
malloc()でメモリを確保してfree()せずに終了するプログラムで試します。

#include <stdio.h>
#include 

int main(void) {

  int a = 100;

  char *str = (char *)malloc(100);
  if (str == NULL) {
    return -1;
  }

  return 0;
}

scan-buildコマンドを実行します。oオプションで解析結果出力先を指定できます。

$ scan-build  --use-analyzer=`which clang` -o . clang -c  clang_memory_leak.c
scan-build: Using '/usr/bin/clang' for static analysis
clang_memory_leak.c:13:10: warning: Potential leak of memory pointed to by 'str'
  return 0;
         ^
1 warning generated.
scan-build: 1 bugs found.
scan-build: Run 'scan-view /xxx/clang/2014-08-30-172653-3486-1' to examine bug reports.

レポートはHTMLで出力されます。以下のような形式。

clang_scan_build

free()を追加しもう一度、静的解析を行ってみます。

$ scan-build  --use-analyzer=`which clang` -o . clang -c  clang_memory_leak_bugfix.c 
scan-build: Using '/usr/bin/clang' for static analysis
scan-build: Removing directory '/xxx/clang/2014-08-30-172808-3530-1' because it contains no reports.
scan-build: No bugs found.

LibClangも面白そうなので時間できればやりたい。