LLVMについて説明する前に一般的なコンパイラのお話から始めます.そんなことはもう知ってるよという人はこの章は読み飛ばしていただいてかまいません.
一般的にコンパイラは,プリプロセッサ,字句解析,構文解析,意味解析,最適化,コード生成といった複数の要素から構成されています.意味解析までをフロントエンドと呼び,ソースコードを解析して中間表現やシンボルテーブルの作成,管理を行います.そしてミドルエンドである最適化部は,フロントエンドから受け取った中間表現に対し,様々な解析や最適化を施してプログラムの高速化を図ります.最後にコード生成部が最適化後の中間表現を目的コード(一般的にはアセンブリ)に変換します.このコード生成部のことをバックエンドと呼びます.あれ?機械語じゃないの?実行ファイルは?と思った方,当然だと思います.たぶん一般的にコンパイラと言ったときに指すものは機械語(実行ファイル)を出力するものです.ではコンパイラじゃなければあれは何なのか?あれはコンパイラドライバだったのさ!はい,というわけで皆さんが普段使っているコンパイラはコンパイラドライバだったのです.
では次の疑問ですね.コンパイラドライバって何だろう?その答えは簡単です.コンパイラ,アセンブラ,リンカなどのプログラムを制御するプログラムです.コンパイラドライバはこれらのプログラムを状況に応じて呼び出します.実行ファイルの生成にはコンパイラが生成したコードをアセンブラで機械語に変換した後,リンカを使用してライブラリ等の外部ファイルをリンクする必要があるわけですが,コンパイラドライバはこれら一連の作業を管理,実行してくれるすごいやつなのです.皆さん良くご存知のgccだってコンパイラドライバだったのです *1.
それではLLVMプロジェクトの概要と構成を説明します.LLVMは2000年にイリノイ大学で開発が開始された,C++で記述されたコンパイラ基盤です.LLVMは現在も開発が進められており,最新版としてLLVM 3.4がリリースされています[2], [3], [4] *6.LLVMプロジェクトは日々成長を続けており,現在のLLVMは多数のサブプロジェクトによって構成されています.LLVMのサブプロジェクトには様々な物が存在しており,サブプロジェクトを組み合わせることでLLVMはコンパイラ基盤としてだけではなく,コンパイラ環境として成立します.
LLVMプロジェクトはBSDライクな自由度の高いライセンスで提供されており,GPLv3の特許関連条項への懸念などから,OSS問わず様々な場面で注目を集めています.
LLVMは,LLVM Core,Clang,LLDBなどの複数のサブプロジェクトから構成されています.ここでは,LLVMプロジェクトを構成するLLVMのサブプロジェクトを幾つか紹介します.*2
LLVM Coreは,LLVMプロジェクトにおいてコンパイラ基盤およびJITコンパイラの機能を提供するプロジェクトであり,その名の通りLLVMのコアとなるサブプロジェクトです.LLVMでは,言語やアーキテクチャから独立した独自の中間表現(以降,LLVM IR)を規定しており,LLVM CoreはLLVM IRに対する最適化機構や様々なアーキテクチャに対するコード生成器を提供します.また,LLVM CoreはLLVM IRに対するJITコンパイラおよびインタプリタの機能も提供しています.これにより,LLVM CoreはLLVM IRに対して静的コンパイラとしてもJITコンパイラとしても使用することが出来ます.本節では,LLVM Coreについて理解を深めるため,次段落でコンパイラ基盤についての説明を行った後,インタプリタ/JITコンパイラおよびコンパイラ基盤としてのLLVM Coreをそれぞれ説明します.
コンパイラ基盤は,その名の通りコンパイラを作成するための共通基盤です.もう少し詳しく言うと,コンパイラ基盤はコンパイラに必要となるモジュール群をまとめたもので,新しい言語に対するコンパイラや最適化アルゴリズム,特定のアーキテクチャに対するバックエンドの実装等に使用します.
コンパイラ基盤はコンパイラを作るためのフレームワークです.例えば,あなたが新しい言語を考えたと想定しましょう.その言語で書いたソースコードを手元のマシンで動かすためにはコンパイラが必要となります.この際,普通はフロントエンドからバックエンドまで全て独自に実装する必要があり,相当面倒な作業になります.既に様々なコンパイラが存在しているのですから,出来れば字句解析や構文解析など,自分の言語特有の部分のみを作ってあとは既存のものを流用したいと思いませんか?それを実現するのがコンパイラ基盤です.コンパイラ基盤は一般的に独自の中間表現を規定しており,その中間表現に対する最適化などの操作や中間表現から機械語への変換などのモジュールおよびそれらを作成するためのライブラリが用意してあります.つまりユーザはコンパイラ基盤の中間表現を生成するためのフロントエンドさえ作れば,あとは既存の資産を再利用することでコンパイラを開発できるのです.また,バックエンド側の実装についてもフロントエンドやミドルエンドを再利用することができるので容易に新たなアーキテクチャ用のコンパイラを試作することができます.これら中間表現とコンパイラ開発のためのモジュールのセットがコンパイラ基盤です.
ちなみにLLVM以外には国産のCOINSというコンパイラ基盤もあります.個人的には多少お世話になったのですが,残念ながらこちらはあまり浸透していないのが現状です.
LLVMは言語非依存の独自の中間表現(以降,LLVM IR)を規定しており,また,LLVM IRに対するインタプリタ/JITコンパイラを有しています.LLVM Coreはこのインタプリタ/JITコンパイラとしてのLLVMの機能を提供しており,Boolean,整数,浮動小数点数,ポインタなどの基本型を格納可能な無限個の仮想レジスタを持つレジスタマシンとして動作します.LLVM Coreは受けとったLLVM IRをインタプリタ,もしくはJITコンパイラとして処理します.JITコンパイラでの動作モードにはEagerとLazyの2つがあり,ユーザは実行方法としてインタプリタ,もしくはEager,Lazyを選択することができます.なお,Eagerは入力されたLLVM IRを最初に全て実行ファイルにコンパイルする方式であり,Lazyは最初に各関数のスタブを作成しておき,最初の関数呼び出しの際に該当関数をコンパイルする方法です.
LLVM CoreにはLLVM IRを最適化し,任意のマシンの機械語やソースコードに変換するためのモジュールやそれらモジュールを作成するためのライブラリやツールが用意されています.ユーザは特定の言語やアーキテクチャに対するフロントエンドやコード生成部を実装することで様々な言語やマシンに対応したコンパイラを開発できます.また,LLVM Coreに用意されたライブラリを用いて最適化部を容易に作成することも可能です*3.ここで,LLVM Coreの構成を図2.1に示します.
図2.1: LLVMの構成
LLVM Coreはフロントエンドで生成されたLLVM IRに対して,Passと呼ばれるモジュールを用いて解析や最適化,オブジェクトコードの生成を行います*4.また,ユーザはLLVMに用意されている各種クラスを利用して独自のPassを実装し,利用することができます.ここで重要なのは,最適化等の操作はLLVM IRに対して行われるものであり,言語やアーキテクチャから独立しているという点です.つまりユーザはフロントエンドやPassを実装し,LLVMで用意されている解析や最適化Passと組み合わせることで自由にコンパイラを開発することができるのです.なお,Passについては第6章で詳しく説明しますので,とりあえず今はPassという名前のものが存在する,ということだけ覚えておいてください.ちなみに図2.1中でフロントエンドがLLVM Coreの外に配置されていることにお気づきの方もいるかと思いますが,LLVM Coreはあくまでコンパイラ基盤ですので,フロントエンドは含まれていません.つまり特定のプログラミング言語の処理系として動作させるためには,該当言語に対するフロントエンドをそれぞれ別途用意する必要があります.LLVMをバックエンドとして利用するフロントエンド実装としては,次に説明するC/C++/Objective-C/Objective-C++のコンパイラであるClangが有名です.
ClangはLLVM Coreをバックエンドとして使用するC/C++/Objective-C/Objective-C++のLLVMフロントエンド実装(コンパイラ)です.FreeBSDがデフォルトのコンパイラとして採用するなど,現在注目が高まってきています.*5エンドユーザとして見た時,Clangには以下の様な特徴があります.
なお,Clangは2013年4月のコミットにてC++11に完全対応しました.バージョン3.2ではまだ完全対応とはなっていませんが,この機能はバージョン3.3のリリースにて盛り込まれました.
LLDBはClangのパーサやLLVMのディスアセンブラなどのLLVMの既存のライブラリを使用したコマンドラインデバッガです.LLDBは再利用可能なコンポーネント群であり,移植性や拡張性を考慮してプラグインアーキテクチャとなっています.なお,LLDBは現在Mac OS XのXcodeでデフォルトのデバッガです.
libc++はlibstdc++を置き換えることを目的としたC++の標準ライブラリです.libc++には下記のような特徴があります.
compiler-rtはlibgccを置き換えることを目的としたライブラリです.compiler-rtには以下の様な特徴があります.
なお,compiler-rtは以下のアーキテクチャをサポートしています.
また,これら全てのアーキテクチャについて,以下のOSをサポートしています.
[*1] 偉そうに言ってみたものの,一般的にgccはコンパイラと言いますし,筆者だって普段はコンパイラドライバなんて言葉は使いません
[*2] 紹介するプロジェクトの選択に関しては特に重要度などの指標はなく,筆者の独断と偏見です
[*3] 一から自分で作成することと比較して,という意味で
[*4] つまりバックエンドもPassの一種ということ
[*5] FreeBSD のカーネルはClangでビルドできることが報告されています
[*6] 最新版はLLVM 3.4ですが本書執筆時の最新バージョンがLLVM 3.2であったため,本書の以降の章ではLLVM 3.2を使用します