LLVM フレームワークで実用的なコンパイラーを作成する: 第 1 回
LLVM とその中間表現を使用してカスタム・コンパイラーを作成する
LLVM (Low Level Virtual Machine) は、任意のプログラミング言語で作成されたプログラムをコンパイル時、リンク時、実行時に最適化するために設計された、極めて強力なコンパイラー・インフラストラクチャー・フレームワークです。LLVM は多岐に渡るプラットフォームで動作し、その最大の特徴である、高速に実行されるコードを生成します。
LLVM フレームワークは、十分なドキュメントが用意された、コードの中間表現 (IR: Intermediate Representation) を中心に構築されています。全 2 回からなる連載の第 1 回目となるこの記事では、LLVM IR の基礎と、その特異な癖のいくつかを詳しく説明した後、LLVM IR の生成作業を自動化するコード・ジェネレーターを作成します。LLVM IR ジェネレーターがあれば、後はお気に入りの言語のフロントエンドをプラグインするだけで、処理フロー全体 (フロントエンドのパーサー + IR ジェネレーター + LLVM バックエンド) が完成します。LLVM によってカスタム・コンパイラーを簡単に作成できるのです!
LLVM の開始準備
記事の本題に入るには、お使いの開発用コンピューターで LLVM がコンパイルされていなければなりません (「参考文献」のリンクを参照)。この記事で使用するサンプル・コードは、LLVM バージョン 3.0 をベースとしています。LLVM
コードのポスト・ビルドおよびインストールでは、llc
と lli
の 2 つが最も重要なツールとなります。
llc と lli
LLVM は仮想マシン (VM) であることから、独自の中間バイト・コード表現があると考えるのが当然でしょう。最終的には、LLVM
のバイト・コードをプラットフォーム固有のアセンブリー言語にコンパイルしなければなりません。そうすることで、プラットフォームにネイティブのアセンブラーとリンカーを使用してアセンブリー・コードを実行し、実行可能ファイルや共有ライブラリーなどを生成することが可能になります。llc
は、LLVM のバイト・コードをプラットフォーム固有のアセンブリー・コードに変換するために使用するツールです
(このツールの詳細については、「参考文献」のリンクを参照)。LLVM
バイト・コードを部分的に直接実行する場合、プログラムにバグが潜んでいるかどうかを知るために、ネイティブ実行可能ファイルが異常な動作を示すようになるまで待つ必要はありません。そのような場合には、バイト・コードを直接実行できる
lli
が役に立ちます。lli
はこの巧みな動作を、インタープリターまたは内部で JIT (Just-In-Time) コンパイラーを使用して実現します。lli
についての詳細は、「参考文献」のリンクを参照してください。
llvm-gcc
llvm-gcc は、-S -emit-llvm
オプションを指定して実行することで LLVM
バイト・コードを生成できるように、GNU コンパイラー・コレクション (gcc) に変更を加えたものです。生成されたバイト・コード (LLVM アセンブリーとも呼ばれます)
は、lli
を使用して実行することができます。llvm-gcc
についての詳細は、「参考文献」を参照してください。お使いのシステムに llvm-gcc がプリインストールされていない場合には、ソースからビルドすることができます。「参考文献」に、その手順をステップ・バイ・ステップで説明しているガイドへのリンクが記載してあるので参照してください。
LLVM による Hello World プログラム
LLVM をより深く理解するためには、LLVM IR とその特異性を学ぶ必要があります。これは、新しいプログラミング言語を学ぶ過程と同様ですが、C
と C++
、そしてそれぞれの特異な癖を経験済みであれば、LLVM IR
の特異性にそれほど怖気づくことはありません。リスト 1 に、コンソール出力に「Hello World」と表示する、LLVM 入門者向けのプログラムを記載します。このコードをコンパイルするには、llvm-gcc を使用します。
リスト 1. お馴染みの Hello World プログラム
#include <stdio.h> int main( ) { printf("Hello World!\n"); }
このコードをコンパイルするためには、以下のコマンドを入力します。
Tintin.local# llvm-gcc helloworld.cpp -S -emit-llvm
コンパイルの完了後、llvm-gcc は helloworld.s ファイルを生成します。lli
を使用してこのファイルを実行すると、メッセージがコンソールに出力されます。lli
の使用方法は以下のとおりです。
Tintin.local# lli helloworld.s Hello, World
リスト 2 に示す初めての LLVM アセンブリー・コードを見てください。
リスト 2. Hello World プログラムの LLVM バイト・コード
@.str = private constant [13 x i8] c"Hello World!\00", align 1 ; define i32 @main() ssp { entry: %retval = alloca i32 %0 = alloca i32 %"alloca point" = bitcast i32 0 to i32 %1 = call i32 @puts(i8* getelementptr inbounds ([13 x i8]* @.str, i64 0, i64 0)) store i32 0, i32* %0, align 4 %2 = load i32* %0, align 4 store i32 %2, i32* %retval, align 4 br label %return return: %retval1 = load i32* %retval ret i32 %retval1 } declare i32 @puts(i8*)
LLVM IR の基礎知識
LLVM には、詳細なアセンブリー言語表現が伴います (「参考文献」のリンクを参照)。前述の Hello World を基にしたオリジナルのプログラムの作成に取り掛かる前に、知っておかなければならない事項を以下に記載します。
- LLVM アセンブリー内のコメントは、セミコロン (
;
) で始まり、行の終わりまで続きます。 - グローバル ID は、アット・マーク (
@
) で始まります。すべての関数名およびグローバル変数も@
で始める必要があります。 - LLVM 内のローカル ID は、パーセント記号 (
%
) で始まります。ID に使用できる通常の正規表現は、[%@][a-zA-Z$._][a-zA-Z$._0-9]*
です。 - LLVM は強い型付けのシステムです。それは、LLVM の最も重要な機能にも言えることです。LLVM は、整数型を
iN
として定義します。N は、その整数のビット幅であり、1 から 223 - 1 までの任意のビット幅を指定することができます。 - ベクトル型または配列型は、
[<要素数> x <各要素のサイズ>]
として宣言します。文字列 “Hello World!” の場合、各文字は 1 バイトであるとし、NULL 文字用の 1 バイトを追加して計上すると、その型は[13 x i8]
になります。 - hello-world 文字列のグローバル文字列定数を宣言するには、
@hello = constant [13 x i8] c"Hello World!\00"
とします。このようにconstant
キーワードを使用して定数を宣言し、その後に型と値を続けます。型についてはすでに説明したので、ここでは値について説明します。値を指定するには、c
の後に二重引用符で囲んだ文字列全体を続けます。二重引用符の中には \0 を含めて、さらに0
で終了してください。文字列を宣言する際には接頭辞c
を使用し、文字列の最後に NULL 文字と 0 の両方を含めなければならない理由については、残念ながら LLVM のドキュメントには説明されていません。LLVM の特異な癖を詳しく調べるには、「参考文献」のリンクから文法ファイルを参照してください。 - LLVM では、ユーザーが関数を宣言して定義することができます。ここでは LLVM
の関数に備わる全機能を網羅する代わりに、必要最小限の要点に絞って説明します。関数を定義するには、
define
キーワードの後に戻り値の型、そして関数名を続けます。32 ビットの整数を返す単純なmain
の定義は、define i32 @main() { ; (i32 を返す何らかの LLVM アセンブリー・コード) }
となります。 - 関数の宣言には、関数の定義と同じく多くの内容が含まれます。例えば、LLVM で
printf
に相当するputs
メソッドの最も単純な宣言は、declare i32 puts(i8*)
です。宣言はdeclare
キーワードで始まり、その後に戻り値の型、関数名、そしてオプションで関数の引数のリストが続きます。宣言は、グローバル・スコープで行う必要があります。 - それぞれの関数は、復帰命令で終わります。復帰命令には、
ret <型> <値>
とret void
の 2 つの形があります。この単純な main ルーチンには、ret i32 0
で十分です。 - 関数を呼び出すには、
call <関数の戻り値の型> <関数名> <関数の引数 (オプション)>
とします。関数のそれぞれの引数の前に、その型を指定することに注意してください。6 ビットの整数を返し、36 ビットの整数を引数に取る test 関数の場合、その構文はcall i6 @test( i36 %arg1 )
となります。
手始めとしては、以上の知識があれば十分です。自作の Hello World プログラムを作成するには、main ルーチン、文字列を格納する定数、そして実際の出力を処理する
puts
メソッドの宣言を定義する必要があります。リスト 3 に、最初に作成してみたプログラムを示します。
リスト 3. 自作の Hello World プログラムを作成する最初の試み
declare i32 @puts(i8*) @global_str = constant [13 x i8] c"Hello World!\00" define i32 @main { call i32 @puts( [13 x i8] @global_str ) ret i32 0 }
以下は、lli
からのログです。
lli: test.s:5:29: error: global variable reference must have pointer type call i32 @puts( [13 x i8] @global_str ) ^
あいにく、思ったような結果にはなりませんでした。何が起こったのでしょう?前述したように、LLVM が強力な型システムであることを思い出してください。つまり、puts
は引数として i8
を指すポインターを想定していましたが、i8
型のベクトルが渡されたため、lli
が即座にエラーを出力したということです。C
プログラミングの経験者であれば、この問題を修正する当然の方法として、型キャストを使用します。そこで話題に上ってくるのが、getelementptr
という LLVM の命令です。リスト 3 の puts
呼び出しは、例えば call i32 @puts(i8* %t)
のように変更しなければならないことに注意してください
(ここで、%t
は i8*
型です。これは、[13 x i8]
から i8*
に型をキャストした結果です。getelementptr
の詳細については、「参考文献」のリンクを参照してください)。先に進む前に、思った通りに動作するコードをリスト
4 に記載します。
リスト 4. getelementptr を使用してポインターへの型キャストを正しく行う
declare i32 @puts (i8*) @global_str = constant [13 x i8] c"Hello World!\00" define i32 @main() { %temp = getelementptr [13 x i8]* @global_str, i64 0, i64 0 call i32 @puts(i8* %temp) ret i32 0 }
getelementptr
には、最初の引数としてグローバル文字列変数へのポインターを渡します。最初のインデックス
i64 0
は、ポインターをグローバル変数までステップ・オーバーするために必要なものです。getelementptr
命令に渡す最初の引数は常に pointer
型の値でなければならないため、最初のインデックスによってそのポインターまでステップ・スルーされます。0
という値は、そのポインターからオフセットされる要素がないことを意味します。私の開発用コンピューターは 64 ビット版 Linux
を実行しているため、このポインターは 8 バイトになります。2 番目のインデックス i64 0
は、文字列の 0
個目の要素を選択するために使用されるもので、この要素が puts
に引数として渡されます。
カスタム LLVM IR コード・ジェネレーターの作成
LLVM IR についての基礎知識は以上で十分ですが、皆さんに必要なのは、LLVM アセンブリーをダンプする自動コード生成システムです。有難いことに、LLVM には、この自動コード生成システムの作成をサポートするアプリケーション・プログラミング・インターフェース (API) が十分に揃っています (「参考文献」でプログラマー向けマニュアルへのリンクを参照してください)。まずは、開発用コンピューターで LLVMContext.h ファイルを見つけてください。このファイルがない場合、LLVM のインストール方法に、何らかの誤りがあった可能性があります。
ここからは、前述した Hello World プログラムの LLVM IR を生成するプログラムの作成に取り掛かります。ここで作成するプログラムは、LLVM API のすべてを扱うわけではありませんが、以降に記載するサンプル・コードから、LLVM API の大半が直観的で簡単に使えることが明らかになるはずです。
LLVM コードにリンクする
LLVM には、llvm-config
という優れたツールが付属しています (「参考文献」を参照)。llvm-config --cxxflags
を実行すると、g++ に渡す必要のあるコンパイル・フラグの情報が得られ、llvm-config --ldflags
を実行するとリンカー・オプションが得られ、llvm-config --libs
を実行すると LLVM
ライブラリーに対してリンクする必要があるオブジェクト・ライブラリーの情報が得られます。リスト 5 に記載するサンプル・コードには、g++ に渡さなければならないすべてのオプションが示されています。
リスト 5. LLVM API を使用してコードをビルドするために llvm-config を利用する
tintin# llvm-config --cxxflags --ldflags --libs \ -I/usr/include -DNDEBUG -D_GNU_SOURCE \ -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS \ -D__STDC_LIMIT_MACROS -O3 -fno-exceptions -fno-rtti -fno-common \ -Woverloaded-virtual -Wcast-qual \ -L/usr/lib -lpthread -lm \ -lLLVMXCoreCodeGen -lLLVMTableGen -lLLVMSystemZCodeGen \ -lLLVMSparcCodeGen -lLLVMPTXCodeGen \ -lLLVMPowerPCCodeGen -lLLVMMSP430CodeGen -lLLVMMipsCodeGen \ -lLLVMMCJIT -lLLVMRuntimeDyld \ -lLLVMObject -lLLVMMCDisassembler -lLLVMXCoreDesc -lLLVMXCoreInfo \ -lLLVMSystemZDesc -lLLVMSystemZInfo \ -lLLVMSparcDesc -lLLVMSparcInfo -lLLVMPowerPCDesc -lLLVMPowerPCInfo \ -lLLVMPowerPCAsmPrinter \ -lLLVMPTXDesc -lLLVMPTXInfo -lLLVMPTXAsmPrinter -lLLVMMipsDesc \ -lLLVMMipsInfo -lLLVMMipsAsmPrinter \ -lLLVMMSP430Desc -lLLVMMSP430Info -lLLVMMSP430AsmPrinter \ -lLLVMMBlazeDisassembler -lLLVMMBlazeAsmParser \ -lLLVMMBlazeCodeGen -lLLVMMBlazeDesc -lLLVMMBlazeAsmPrinter \ -lLLVMMBlazeInfo -lLLVMLinker -lLLVMipo \ -lLLVMInterpreter -lLLVMInstrumentation -lLLVMJIT -lLLVMExecutionEngine \ -lLLVMDebugInfo -lLLVMCppBackend \ -lLLVMCppBackendInfo -lLLVMCellSPUCodeGen -lLLVMCellSPUDesc \ -lLLVMCellSPUInfo -lLLVMCBackend \ -lLLVMCBackendInfo -lLLVMBlackfinCodeGen -lLLVMBlackfinDesc \ -lLLVMBlackfinInfo -lLLVMBitWriter \ -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen \ -lLLVMX86Desc -lLLVMX86AsmPrinter -lLLVMX86Utils \ -lLLVMX86Info -lLLVMAsmParser -lLLVMARMDisassembler -lLLVMARMAsmParser \ -lLLVMARMCodeGen -lLLVMARMDesc \ -lLLVMARMAsmPrinter -lLLVMARMInfo -lLLVMArchive -lLLVMBitReader \ -lLLVMAlphaCodeGen -lLLVMSelectionDAG \ -lLLVMAsmPrinter -lLLVMMCParser -lLLVMCodeGen -lLLVMScalarOpts \ -lLLVMInstCombine -lLLVMTransformUtils \ -lLLVMipa -lLLVMAnalysis -lLLVMTarget -lLLVMCore -lLLVMAlphaDesc \ -lLLVMAlphaInfo -lLLVMMC -lLLVMSupport
LLVM モジュール、コンテキスト、その他
LLVM モジュール・クラスは、他のすべての LLVM IR オブジェクトの最上位レベルのコンテナーです。LLVM モジュール・クラスには、グローバル変数、関数、そのモジュールが依存する他のモジュール、シンボル・テーブルなどのリストを含めることができます。LLVM モジュールのコンストラクターは以下のとおりです。
explicit Module(StringRef ModuleID, LLVMContext& C);
作成するプログラムは、まず LLVM モジュールを作成するところから始める必要があります。最初の引数は、モジュールの名前です。これは任意のダミー文字列で構いません。2
番目の引数は、LLVMContext
と呼ばれるものです。LLVMContext
は若干わかりにくいクラスですが、変数などを作成するコンテキストを提供するクラスであることを理解していれば十分です。このクラスは、スレッドごとにローカル・コンテキストを作成する必要があるような、複数のスレッドが関与するコンテキストで重要になってきます。この場合、各スレッドは他のあらゆるコンテキストとは完全に独立して実行されます。ここではとりあえず、LLVM が提供するデフォルトのグローバル・コンテキスト・ハンドルを使用します。モジュールを作成するコードを以下に記載します。
llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module* module = new llvm::Module("top", context);
知っておくべき次の重要なクラスは、IRBuilder
です。このクラスは、LLVM
命令を作成して、これらの命令を基本ブロックに挿入するための API を実際に提供します。IRBuilder
には多数のオプションがありますが、私はこのクラスを作成するのにおそらく最も簡単な方法として、グローバル・コンテキストを渡すことにしました。それには、以下のコードを使用します。
llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module* module = new llvm::Module("top", context); llvm::IRBuilder<> builder(context);
LLVM オブジェクト・モデルが用意できたら、モジュールの dump
メソッドを呼び出すことで、その内容をダンプすることができます。リスト 6 にコードを記載します。
リスト 6. ダミー・モジュールを作成する
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module* module = new llvm::Module("top", context); llvm::IRBuilder<> builder(context); module->dump( ); }
リスト 6 のコードを実行すると、以下の内容がコンソールに出力されます。
; ModuleID = 'top'
次に作成しなければならないのは、main
メソッドです。LLVM には、関数を作成するための llvm::Function
、そして関数の戻り値の型を関連付けるための llvm::FunctionType
があります。また、main
メソッドはモジュールの一部として含めなければなければならないことに注意してください。リスト 7 にコードを記載します。
リスト 7. main メソッドを最上位のモジュールに追加する
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module *module = new llvm::Module("top", context); llvm::IRBuilder<> builder(context); llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getInt32Ty(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); module->dump( ); }
main
が void
を返すようにするために、builder.getVoidTy()
を呼び出していることに注意してください 。main
が i32
を返すようにするのであれば、builder.getInt32Ty()
を呼び出すことになるはずです。リスト 7 のコードをコンパイルして実行した結果は、以下のとおりです。
; ModuleID = 'top' declare void @main()
main
が実行するはずの一連の命令はまだ定義していません。そこで、これから基本ブロックを定義して main
メソッドに関連付けます。基本ブロックは、LLVM IR の命令を集めたもので、そのコンストラクチャーの一部としてラベル
(C
のラベルと同様) を定義するオプションがあります。builder.setInsertPoint
が、LLVM エンジンに対して次に命令を挿入する場所を指示します。リスト 8 にコードを記載します。
リスト 8. 基本ブロックを main に追加する
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module *module = new llvm::Module("top", context); llvm::IRBuilder<> builder(context); llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getInt32Ty(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc); builder.SetInsertPoint(entry); module->dump( ); }
リスト 8 の出力を以下に記載します。main
の基本ブロックが定義されているため、LLVM ダンプは main
を宣言としてではなく、メソッド定義として扱うことに注目してください。何と賢いことでしょう!
; ModuleID = 'top' define void @main() { entrypoint: }
今度はグローバル hello-world 文字列をコードに追加します。リスト 9 にコードを記載します。
リスト 9. グローバル文字列を LLVM モジュールに追加する
#include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Support/IRBuilder.h" int main() { llvm::LLVMContext& context = llvm::getGlobalContext(); llvm::Module *module = new llvm::Module("top", context); llvm::IRBuilder<> builder(context); llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getVoidTy(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc); builder.SetInsertPoint(entry); llvm::Value *helloWorld = builder.CreateGlobalStringPtr("hello world!\n"); module->dump( ); }
リスト 9 による出力では、LLVM エンジンが文字列を以下のように出力することに注目してください。
; ModuleID = 'top' @0 = internal unnamed_addr constant [14 x i8] c"hello world!\0A\00" define void @main() { entrypoint: }
残る作業は、puts
メソッドを宣言して呼び出すだけです。puts
メソッドを宣言するには、このメソッドに適切な FunctionType*
を作成する必要があります。当初の Hello
World コードでご存知のとおり、puts
が i32
を返し、i8*
を入力引数として受け入れます。リスト 10 に、puts
の適切な型を作成するコードを記載します。
リスト 10. puts メソッドを宣言するコード
std::vector<llvm::Type *> putsArgs; putsArgs.push_back(builder.getInt8Ty()->getPointerTo()); llvm::ArrayRef<llvm::Type*> argsRef(putsArgs); llvm::FunctionType *putsType = llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false); llvm::Constant *putsFunc = module->getOrInsertFunction("puts", putsType);
FunctionType::get
には最初の引数として戻り値の型を渡し、2 番目の引数として LLVM::ArrayRef
構造を、最後の引数として false
を渡しています。false は、この後には可変数の引数が続かないことを示します。ArrayRef
構造は、データが含まれていないことを除き、ベクトルと同様です。この ArrayRef
構造は配列やベクトルと同じく、主にデータ・ブロックをラップするために使用されます。以上の変更を加えた後、出力はリスト 11 のようになります。
リスト 11. puts メソッドを宣言する
; ModuleID = 'top' @0 = internal unnamed_addr constant [14 x i8] c"hello world!\0A\00" define void @main() { entrypoint: } declare i32 @puts(i8*)
後は、puts
メソッドを main
の中で呼び出して、main
から戻れば完成です。キャストおよび残りの処理は、LLVM API が引き受けてくれます。puts
を呼び出すには、builder.CreateCall
を呼び出せば良いのです。最後に、復帰命令を作成するために、builder.CreateRetVoid
を呼び出します。リスト 12 に、完全に機能するコード全体を記載します。
リスト 12. 「Hello World」を出力する完全なコード
#include "llvm/ADT/ArrayRef.h" #include "llvm/LLVMContext.h" #include "llvm/Module.h" #include "llvm/Function.h" #include "llvm/BasicBlock.h" #include "llvm/Support/IRBuilder.h" #include <vector> #include <string> int main() { llvm::LLVMContext & context = llvm::getGlobalContext(); llvm::Module *module = new llvm::Module("asdf", context); llvm::IRBuilder<> builder(context); llvm::FunctionType *funcType = llvm::FunctionType::get(builder.getVoidTy(), false); llvm::Function *mainFunc = llvm::Function::Create(funcType, llvm::Function::ExternalLinkage, "main", module); llvm::BasicBlock *entry = llvm::BasicBlock::Create(context, "entrypoint", mainFunc); builder.SetInsertPoint(entry); llvm::Value *helloWorld = builder.CreateGlobalStringPtr("hello world!\n"); std::vector<llvm::Type *> putsArgs; putsArgs.push_back(builder.getInt8Ty()->getPointerTo()); llvm::ArrayRef<llvm::Type*> argsRef(putsArgs); llvm::FunctionType *putsType = llvm::FunctionType::get(builder.getInt32Ty(), argsRef, false); llvm::Constant *putsFunc = module->getOrInsertFunction("puts", putsType); builder.CreateCall(putsFunc, helloWorld); builder.CreateRetVoid(); module->dump(); }
まとめ
この LLVM の導入編では、lli
や llvm-config
などの LLVM ツールについて学び、LLVM 中間コードの内容を調べた後、LLVM API を使用して中間コードを自動的に生成しました。この連載の最終回となる第 2 回では、LLVM を利用することができる別の新しいタスクを詳しく探ります。それは、必要最小限の作業で別のコンパイル・パスを追加することです。
ダウンロード可能なリソース
関連トピック
- LLVM コンパイラーの連載として、LLVM について踏み込んだ内容の「LLVM フレームワークで実用的なコンパイラーを作成する: 第 2 回 C/C++ コードのプリプロセスに clang を使用する」(Arpan Sen 著、developerWorks、2012年6月) では、clang API を使用して C/C++ コードのプリプロセスを行えるようにコンパイラーを変更します。
- LLVM を知る絶好の手引書として、公式の LLVM Tutorial を利用してください。
- LLVM の開発についての詳細は、「The Architecture of Open Source Applications」の Chris Lattner が書いた章を参照してください。
- 重要な 2 つの LLVM ツール、
llc
とlli
について詳しく学んでください。 - ステップ・バイ・ステップで手順を説明している「Building llvm-gcc from Source」で、 llvm-gcc ツールの詳細を調べて、このツールをソースからビルドする方法を学んでください。
- 「LLVM Language Reference Manual」で、LLVM アセンブリー言語の詳細について読んでください。
- LLVM のグローバル文字列の定数について詳しく学ぶには、「Log of /llvm/trunk/utils/llvm.grm」でその文法ファイルを調べてください。
- 「The Often Misunderstood GEP Instruction」マニュアルで、
getelementptr
命令について詳しく学んでください。 - LLVM API に欠かせない資料、「LLVM Programmer's Manual」を徹底的に調べてください。
- LLVM コンパイルのオプションを出力する
llvm-config
ツールについての説明を読んでください。 - developerWorks Open source ゾーンには、オープソースのツールおよびオープンソース技術の使用に関する情報が豊富に揃っています。
- developerWorks Linux ゾーンで、Linux 開発者および管理者向けのハウツー記事とチュートリアル、そしてダウンロード、ディスカッション、フォーラムなど、豊富に揃った資料を探してください。
- developerWorks Web development に、多種多様な Web ベースのソリューションを話題にした記事が揃っています。
- Twitter で developerWorks をフォローするか、developerWorks で Linux に関するツイートのフィードに登録してください。
- ご自分に最適な方法で IBM 製品を評価してください。評価の方法としては、製品の試用版をダウンロードすることも、オンラインで製品を試してみることも、クラウド環境で製品を使用することもできます。また、SOA Sandbox では、数時間でサービス指向アーキテクチャーの実装方法を効率的に学ぶことができます。