编译器 llvm clang 源码转换示例
編譯器 llvm clang 源碼轉換示例
從git獲取llvm項目的源碼方式:
git clone https://github.com/llvm/llvm-project.git
下載源碼后,進入llvm-project目錄包括如下內容:
llvm-project/llvm目錄包括如下內容:
CLANG實戰
實戰 利用Clang制作自己的編譯器 source-to-source 源代碼轉換
參考:
https://github.com/Ewenwan/llvm-clang-samples/blob/master/src_clang/tooling_sample.cpp
void foo(int* a, int b) {
if (a[0] > 1)
{
b[0] = 2;
}
}
void bar(float x, float y); // just a declaration
自動添加 添加注釋
// Begin function foo returning void
void foo(int a, int *b) {
if (a[0] > 1) // the ‘if’ part
{
b[0] = 2;
}
}
// End function foo
void bar(float x, float y); // just a declaration
LLVM實戰
函數簽名
C語言中的函數簽名由以下幾部分組成:
? 返回類型
? 函數名
? 參數個數及參數類型
比如
int add(int a, int b) {
return a + b;
}
這段C程序代碼中的add函數的函數簽名就是int add(int, int)
待處理的C程序代碼
#include <stdio.h>
#include <stdlib.h>
void keep() {
printf("\n");
}
int add(int a, int b) {
return a + b;
}
int* getArr(int n) {
return (int*)malloc(sizeof(int) * n);
}
int main(int argc, char** argv) {
return 0;
}
項目運行結果是
在待處理的代碼中定義了包括main函數在內的四個函數,但是最終結果卻是六個函數,這是因為調用了C標準庫中的printf函數和malloc函數,編譯器在預處理階段將這兩個函數的聲明加入到了代碼中。
另外一個值得關注之處是,與C語言中int、char等類型不同,打印出來的函數簽名中的類型是i32、i8,這其實是因為我們首先需要把待處理的C程序代碼轉換為LLVM IR字節碼,然后才會用自定義LLVM項目對其進行處理,打印出來的類型其實是LLVM IR的類型,除此之外,long對應i64、float對應f32、double對應f64,不過LLVM IR void和指針兩種類型還是與C語言相同的。
函數簽名
C語言中的函數簽名由以下幾部分組成:
返回類型 函數名 (參數個數及參數類型)
// 本程序 輸入 llvm IR文件 輸出 IR中的函數簽名
// 輸入的IR文件 可以由clang編譯得到
// 例如 clang -emit-llvm -c test.c -o test.bc // test.c為測試程序
// 本程序編譯命令
// clang++ $(llvm-config --cxxflags --ldflags --libs) main.cpp -o main
// 運行程序
// ./main test.bc
// 引入相關LLVM頭文件
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Module.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/CommandLine.h>
using namespace llvm;
// LLVM上下文全局變量
static ManagedStatic GlobalContext;
// 命令行位置參數全局變量, 這個參數的含義是需要處理的LLVM IR字節碼的文件名
static cl::optstd::string InputFilename(cl::Positional, cl::desc(".bc"), cl::Required);
int main(int argc, char **argv) {
// 診斷實例
SMDiagnostic Err;
// 格式化命令行參數,
cl::ParseCommandLineOptions(argc, argv);
// 讀取并格式化LLVM IR字節碼文件, 返回LLVM Module(Module是LLVM IR的頂級容器)
std::unique_ptr M = parseIRFile(InputFilename, Err, *GlobalContext);
// 錯誤處理
if (!M) {
Err.print(argv[0], errs());
return 1;
}
// 遍歷Module中的每一個Function
for (Function &F:*M) { // c++ 語法 范圍for F是 IR模塊中的每一個函數的引用
// 過濾掉那些以llvm.開頭的無關函數
if (!F.isIntrinsic()) {
// 打印函數返回類型
outs() << *(F.getReturnType());
// 打印函數名
outs() << ’ ’ << F.getName() << ‘(’; // 函數名有可能和c文件里的不同(加了一些屬性描述)
// 遍歷函數的每一個參數
for (Function::arg_iterator it = F.arg_begin(), ie = F.arg_end(); it != ie; it++) {
// 打印參數類型
outs() << *(it->getType());
if (it != ie - 1) {
outs() << ", ";
}
}
outs() << “)\n”;
}
}
}
項目編譯運行
在編譯項目之前,需要確認一下編譯運行環境 :
? 操作系統:Ubuntu 18.04 64位
? LLVM版本:9.0.0
? 待處理的C程序代碼文件:test.c
? 項目代碼文件:main.cpp
然后獲取待處理的C程序代碼的LLVM IR字節碼
clang -emit-llvm -c test.c -o test.bc
再編譯項目代碼
clang++ $(llvm-config --cxxflags --ldflags --libs) main.cpp -o main
最后運行得到上文圖示的結果
./main test.bc
參考鏈接:
https://www.freesion.com/article/3548547366/
https://www.freesion.com/article/4240352588/
https://zhuanlan.zhihu.com/p/102270840
https://github.com/Ewenwan/llvm-clang-samples/blob/master/src_clang/tooling_sample.cpp
總結
以上是生活随笔為你收集整理的编译器 llvm clang 源码转换示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Cache Memory技术示例
- 下一篇: 云计算灾备原理与预防恢复方案