而是精簡版, 採用建置方式是將 pass 的程式碼獨立於 LLVM 的 Source tree 外
這樣的好處是可避免與整串 LLVM 搞在一起, 並且對於整體建置流程會較為清楚
這篇文章對於讀者有些假設 :
- 已經會建置 LLVM (不會? 建置與安裝 LLVM + Clang)
- 對於 Makefile 有一點基礎知識 (沒有? 快來惡補)
1. 先從 LLVM 抄一個 Hello Pass
#define DEBUG_TYPE "hello" #include "llvm/Pass.h" #include "llvm/IR/Function.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/Statistic.h" using namespace llvm; namespace { // Hello - The first implementation, without getAnalysisUsage. struct Hello : public FunctionPass { static char ID; // Pass identification, replacement for typeid Hello() : FunctionPass(ID) {} virtual bool runOnFunction(Function &F) { errs() << "Hello: "; errs() << F.getName() << '\n'; return false; } }; } char Hello::ID = 0; static RegisterPass<Hello> X("hello", "Hello World Pass");將上列程式碼儲存成 Hello.cpp 或著任何你喜歡的檔名
原程式碼上面有一些授權宣告跟註解, 基於文章版面配置將那部份移除, 其詳細授權見LLVM Developer Policy
說明?能建置成功前先把細節拋一邊吧
2. 建置 Makefile
LLVM_CONFIG=llvm-config CXX=`$(LLVM_CONFIG) --bindir`/clang CXXFLAGS=`$(LLVM_CONFIG) --cppflags` -fPIC -fno-rtti LDFLAGS=`$(LLVM_CONFIG) --ldflags` all: hello.so hello.so: Hello.o $(CXX) -shared Hello.o -o hello.so $(LDFLAGS) -fPIC Hello.o: Hello.cpp $(CXX) -c Hello.cpp -o Hello.o $(CXXFLAGS) clean: rm -f *.o hello.so把上面內容抄到與 Hello.cpp 同目錄下的 Makefile 中
注意一下 Makefile 建置動作前面的是 Tab 字元不是空白字元, 相當重要
這邊唯一要注意的是 LLVM_CONFIG 要設定為 你安裝的 llvm 路徑裡面的那個 llvm-config
例如 LLVM 裝在家目錄底下, 那麼 LLVM_CONFIG 就設定為 /home/kito/bin/llvm-config
3. 建置與測試
接著執行 make,make應該會吐出下面兩行
`llvm-config --bindir`/clang -c Hello.cpp -o Hello.o `llvm-config --cppflags` -fPIC `llvm-config --bindir`/clang -shared Hello.o -o hello.so `llvm-config --ldflags` -fPIC然後目錄下應該會出現 Hello.so 才對
ls # Hello.cpp Hello.o hello.so Makefile接著寫個 Hello World 的小程式
#include <stdio.h> int main () { printf("Hello LLVM!\n"); }將以上簡單內容存檔成 HelloWorld.c 接著把 Hello World 產生 .bc 檔, 加上 -emit-llvm 告訴 llvm 你要產生 bitcode
clang -c -emit-llvm -o HelloWorld.bc HelloWorld.c最後步驟就是把你的 HelloWorld.bc 丟到前面寫的 Hello Pass !
opt -load ./hello.so -hello HelloWorld.bc -o /dev/null後面 -o /dev/null 的意思是叫 opt 把經過最佳化或分析的結果直接丟掉
執行完後 LLVM 就會跟你說 Hello 了!
Hello: mainmain 是你 Hello World 中的 Function 名稱
4. 回頭看一下 Hello Pass
DEBUG_TYPE 是 LLVM 拿來統計該 pass 被呼叫幾次或著是使用 DEBUG 輸出除錯訊息的時候用的#define DEBUG_TYPE "hello"include 要用到的 LLVM 的
#include "llvm/Pass.h" #include "llvm/IR/Function.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/Statistic.h"把 llvm namespace 的東西都弄進來, 雖然這是不好的習慣, 不過由於方便起見直接 using 下去吧XD
using namespace llvm;匿名 namespace, 避免 export 裡面的 symbol,
聽無上一句在講啥就先跳過就可以了
namespace {Hello 繼承 FunctionPass, 代表 Hello Pass 的輸入是以 Function 為單位
// Hello - The first implementation, without getAnalysisUsage. struct Hello : public FunctionPass {現階段下面這行就照抄吧
給 LLVM 用來識別這個 Pass 的 id
static char ID; // Pass identification, replacement for typeidConstructor
Hello() : FunctionPass(ID) {}這邊則是整個 pass 的精華所在, LLVM 會以一次一個 Function 為單位, 餵進來給你玩
大致上裡面的行為就是將餵進來的 Function 的名稱印出來而已
其中 return false 是告訴 LLVM 你沒有修改任何東西
virtual bool runOnFunction(Function &F) { errs() << "Hello: "; errs() << F.getName()) << '\n'; return false; }class 跟 namespace 的結束, 沒啥好說的
}; }定義下一下 Hello::ID 的實體
char Hello::ID = 0;跟 LLVM 註冊你的這個 Pass, 第一個參數是 pass name, 第二個則是說明用的文字
static RegisterPass<hello> X("hello", "Hello World Pass");
5. 解析 Makefile
先指定 llvm-config 執行檔的所在位置, 如果 llvm-config 已經加到 PATH 中的話那就填 llvm-config 就好LLVM_CONFIG=llvm-configllvm-config 這隻程式可以幫你產生出大部分你所需的 flag
用 --help 可以檢視所有可用選項
llvm-config --help指定編譯器使用 clang, 單純個人喜好XD
CXX=`$(LLVM_CONFIG) --bindir`/clang指定編譯的參數, 大部分從 llvm-config --cppflags 即可,
但由於我們現在是要編譯的是 Shared Library 所以要加 -fPIC
不知道 Shared Library 是啥? 快去弄一本程式設計師的自我修養來 K 阿~~
-fno-rtti 則是指定不產生 Run-time Typeinfo 的資訊,加了此選項後 typeid 及 dynamic_cast 會無法使用,基於實作成本 LLVM 在這方面有實作自己一套的 RTTI 機制
CXXFLAGS=`$(LLVM_CONFIG) --cppflags` -fPIC -fno-rtti指定 Link 的參數, 大部分從 llvm-config --ldflags 即可,
現在是要編譯的是 Shared Library 所以加 -shared
LDFLAGS=`$(LLVM_CONFIG) --ldflags` -shared我們總共要建置 hello.so
all: hello.sohello.so 怎麼生勒?, 生之前要先生出 Hello.o
然後下面一行則是建置規則
hello.so: Hello.o $(CXX) Hello.o -o hello.so $(LDFLAGS)Hello.o 的建置規則, 以及其相依 Hello.cpp
Hello.o: Hello.cpp $(CXX) -c Hello.cpp -o Hello.o $(CXXFLAGS)寫 Makefile 時寫個 clean 的 rule 是個好習慣 :)
clean: rm -f *.o hello.so
參考
[1] Writing an LLVM Pass[2] 程式設計師的自我修養-連結、載入、程式庫
Update Note
- (2012/11/16) 在較新版的 llvm-config 無提供 -fno-rtti 選項,必須自行在 CXXFLAGS 那邊自行添加
- (201211/22) 上面提到那個項目目前不確定是 bug 還是怎樣,cmake 出來的 llvm-config 不會有 -fno-rtti,但 configure 建置出來的 llvm-config 則會有
- (2018/10/18) 更新範例中的 include path, 已測試 於 LLVM 6 及 7
多謝同胞
回覆刪除給我推薦一些好的中文版的llvm站點哈
回覆刪除