LLVM 初探

2021-02-20 程式設計師大咖

0x1. LLVM 架構簡介
經典的三段式設計LLVM,GCC, JIT(Java, Python) 等編譯器都遵循經典的三段式設計• 前端 (Frontend) - 詞法分析,語法分析, 生成抽象語法樹,生成中間語言 (例如 java 的字節碼,llvm 的 IR,GCC 的 GIMPLE Tuples)
• 優化器 (Optimizer) - 分析中間語言,避免多餘的計算,提高性能;• 後端 (Backend) - 根據中間語言,生成對應的 CPU 架構指令 例如 X86,ARM;通過這種設計,增加新的語言,只需要實現新的 Frontend,Optimizer 和 Backend 可以重用;同理新增新的 CPU 架構時,也只需要實現新的 Backend。LLVM 的優勢
LLVM,GCC,JIT 都採用三段式設計,LLVM 的優勢在哪裡 ?

古老,模塊化不夠 (GCC 是個整體,無法獨立使用某塊功能)。

強制 JIT 編譯,垃圾回收以及使用非常特殊的對象模型;在編譯與該模型不完全匹配的語言(例如C)時,性能欠佳。(我也不懂 😊)

• 更多請參考 https://www.kanxue.com/book-37-410.html0x2: 編譯 LLVM
cmake, python, 可以通過 homebrew 來安裝詳細要求參考 Getting Started with the LLVM System - Requirements

git clone --depth=1 https://github.com/llvm/llvm-project.git

cd llvm-project
mkdir build_with_ninja
cd build_with_ninja
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt" -G Ninja ../llvm
ninja

• -DCMAKE_BUILD_TYPE=Release

• 默認 Debug,設置為 Release 可以減少硬碟空間的佔用

• -DLLVM_ENABLE_ASSERTIONS=ON

• -DCMAKE_INSTALL_PREFIX=directory

• 不指定,默認安裝在 build_with_ninja 的 bin 目錄下

• Ninja - 推薦,編譯速度更快
• 安裝方式- brew install ninja
• Unix Makefiles - 通過 make 來編譯
• Visual Studio,Xcode - 方便調試

0x3. Clang 命令1. 首先創建一個名為 "hello.c" 的 C 文件

#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}

clang -fsyntax-only -Xclang -dump-tokens hello.c

clang -fsyntax-only -Xclang -ast-dump hello.c

clang -o3 -emit-llvm hello.c -c -o hello.bc

-emit-llvm 搭配 -S 或者 -c 選項可以生成 LLVM .ll 或者 .bc 文件,.ll, .bc 都是 LLVM IR 格式,它們的區別是 .ll 是可讀的,而 .bc 不可讀。

llvm-dis < hello.bc | less

10. 將 LLVM 中間文件 (.ll 或者 .bc) 編譯為彙編文件

clang hello.bc -S hello.s

fatal error: 'stdio.h' file not found
#include <stdio.h>
^~~~~~~~~
1 error generated.

clang -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include ...

0x4. 上手 Passpass 是什麼?一系列優化 和 轉換 LLVM IR 的 C++ 代碼, 主要流程如下:• Module: 包含 Function, 全局變量等

for (Module::iterator iter = M.begin(); iter != M.end(); iter++) {
Function *F = &(*iter);

• Function:包含若干 BasicBlock• BasicBlock:包含若干 Instruction• Instruction: 指令,包含操作,Value

• Value:大部分對象都可以看成 Value,包括常量,參數,指令,函數在 LLVM 源碼目錄外開發 Pass為方便表述,簡稱 「外部 pass」,外部 pass 比較靈活,不用修改 LLVM 源碼配置;嘗試製作一個列印函數名的簡單 Pass,步驟如下: 

# llvm-project 同級目錄
cd ../..
mkdir outpasses

outpasses/
|
CMakeLists.txt
PrintFunctions/
|
CMakeLists.txt
PrintFunctions.cpp
...

3. outpasses / CMakeLists.txt 內容如下:配置 LLVM_DIR - LLVM_DIR 為 LLVM 編譯時的安裝目錄

cmake_minimum_required(VERSION 3.4)

set(ENV{LLVM_DIR} ~/llvm/llvm-project/build_with_ninja/lib/cmake/llvm)

find_package(LLVM REQUIRED CONFIG)
add_definitions(${LLVM_DEFINITIONS})
include_directories(${LLVM_INCLUDE_DIRS})
link_directories(${LLVM_LIBRARY_DIRS})

# add c++ 14 to solve "error: unknown type name 'constexpr'"
add_compile_options(-std=c++14)
add_subdirectory(PrintFunctions) # Use your pass name here.

4. PrintFunctions/CMakeLists.txt 內容如下:

add_library(PrintFunctions MODULE
# List your source files here.
printFunctions.cpp
)

# LLVM is (typically) built with no C++ RTTI. We need to match that;
# otherwise, we'll get linker errors about missing RTTI data.
set_target_properties(PrintFunctions PROPERTIES
COMPILE_FLAGS "-fno-rtti"
)

# Get proper shared-library behavior (where symbols are not necessarily
# resolved when the shared library is linked) on OS X.
if(APPLE)
set_target_properties(PrintFunctions PROPERTIES
LINK_FLAGS "-undefined dynamic_lookup"
)
endif(APPLE)

5. PrintFunctions/printFunctions.cpp 內容如下:

#include "llvm/Pass.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Transforms/IPO/PassManagerBuilder.h"
using namespace llvm;

namespace {
struct Hello : public FunctionPass {
static char ID;
Hello() : FunctionPass(ID) {}

virtual bool runOnFunction(Function &F) {
errs() << "I saw a function called " << F.getName() << "!\n";
return false;
}
};
}

// Automatically enable the pass.
char Hello::ID = 0;
static RegisterPass<Hello> X("hello", "Hello World Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);
static RegisterStandardPasses Y(
PassManagerBuilder::EP_EarlyAsPossible,
[](const PassManagerBuilder &Builder,
legacy::PassManagerBase &PM) { PM.add(new Hello()); });

6. 編譯 pass 得到 libPrintFunctions.so

libPrintFunctions.so 在 PrintFunctions 文件夾中

cd llvm-project
./build_with_ninja/bin/clang -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -Xclang -load -Xclang ../outpasses/PrintFunctions/libPrintFunctions.so ../llvmtest/test.c

#include <stdio.h>
int main() {
int a = 0;
if ( a = 1) {
printf("123");
}
return 0;
}

I saw a function called main!

./build_with_ninja/bin/clang -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk/usr/include -o3 -emit-llvm test.c -c -o test.bc

./build_with_ninja/bin/opt -load ../outpasses/PrintFunctions/libPrintFunctions.so -hello < ../llvmtest/test.bc

Pass 和 PassManager 的關係PassManager 管理 Pass,解決多個 Pass 依賴,傳值等問題,例如 PassA 需要等待 PassB 執行完成後才執行;Pass 需要註冊到 PassManager 中:• 註冊到 opt 中, hello 命令行可選參數,「Hello World Pass」 幫助說明

# 註冊到 opt 中,
static RegisterPass<Hello> X("hello", "Hello World Pass",
false /* Only looks at CFG */,
false /* Analysis Pass */);

通過 opt -hello 來使用 HelloPass

opt -load lib/LLVMHello.so -hello < hello.bc > /dev/null

• 註冊到標準編譯流程中,默認會執行 HelloPass,例如通過 clang 調用

static llvm::RegisterStandardPasses Y(
llvm::PassManagerBuilder::EP_EarlyAsPossible,
[](const llvm::PassManagerBuilder &Builder,
llvm::legacy::PassManagerBase &PM) { PM.add(new Hello()); });

移植到 LLVM 源碼目錄• 拷貝 PrintFunctions 目錄到 LLVM 源碼目錄的 lib/Transform 目錄下• 在 lib/Transform/CMakeLists.txt 中新增 add_subdirectory(PrintFunctions)• 重新編譯,可以得到 libPrintFunctions.so 通過 Xcode 調試 Pass參考• 了解 LLVM 是什麼,基本架構模塊,IR 是什麼,pass 的作用

• 閱讀 LLVM Overview 了解 LLVM 的模塊劃分以及各模塊基本功能。
• 閱讀 The Architecture of Open Source Applications 了解 LLVM 的架構

• 閱讀 get_started 了解如何編譯 LLVM
• 閱讀 llvm cmake 參數 了解編譯時 cmake 參數的含義
• 閱讀 CMake 簡介 了解 CMake 語法

• 根據 writing an LLVM pass 上手嘗試,了解 pass 的使用流程
• ProgrammersManual 閱讀開發手冊了解編寫 pass 可能涉及到的 API
• IR 手冊 略讀 IR 手冊,了解 IR 的基本特徵

• LLVM 中文網
• Xcode調試一個LLVM Pass
• LeadroyaL's llvm 系列文章

• llvm cmake 參數
• IR 手冊
• LLVM command guide llvm-as, llvm-dis, opt 等命令的使用說明

 推薦↓↓↓ 

涵蓋:程式設計師大咖、源碼共讀、程式設計師共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程式設計師等。

相關焦點

  • 【線上問題定位】Llvm庫ARM環境崩潰問題
    崩潰信息為/xxx/llvm6.0/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp:383: void llvm::RuntimeDyldELF::resolveAArch64Relocation(const llvm::SectionEntry&, uint64_t, uint64
  • 自定義 LLVM PASS 實現 函數耗時插樁統計
    echo "ninja build llvm ..."-11.0.0/llvm/lib/Transforms 目錄將 Hello 目錄拷貝一份,並重命名為 FunctionCallTime在 llvm-project-llvmorg-11.0.0/llvm/lib/Transforms/CMakeLists.txt 後面追加一行 add_subdirectory(FunctionCallTime)在 llvm-project-llvmorg
  • 關於LLVM,這些東西你必須要知道!
    二、安裝編譯LLVM這裡使用clang作為前端:1.直接從官網下載:http://releases.llvm.org/download.html2.svn獲取svn co http://llvm.org/svn/llvm-project/llvm/trunk llvmcd llvm/toolssvn co http://llvm.org/svn
  • 自定義 LLVM PASS 實現函數耗時插樁統計
    文章地址:https://blog.0x1306a94.com/docs/llvm/ch01/01/源碼地址:https://github.com/0x1306a94/LLVMFunctionCallTimePass原理原理很簡單
  • LLVM 7.0.0 發布,提升性能分析能力 - OSCHINA - 中文開源技術交流...
    OpenCL C ++支持FreeBSD 支持 MSan、X-Ray 和 libFuzzerUBSan 檢查隱式轉換很多 lld 中修復了長尾兼容性問題,為目前正在生產的 ELF、COFF 和 MinGW 做好準備新工具 llvm-exegesis
  • LLVM 3.9 發布,編譯器架構 - OSCHINA - 中文開源技術交流社區
    使用文檔:http://llvm.org/releases/3.9.0/docs/index.html下載地址:http://llvm.org/releases/download.html#3.9.0
  • 什麼是 LLVM?Swift, Rust, Clang 等語言背後的支持
    (點擊上方公眾號,可快速關注)英文:Serdar Yegulalp,翻譯:開源中國翻譯www.oschina.net/translate/what-is-llvm-the-power-behind-swift-rust-clang-and-more
  • 《獵人手遊》初探失美樂開荒打法教學攻略 怎麼打初探失美樂
    獵人手遊初探失美樂打法攻略 初探失美樂副本 我們在進圈之後,古鈞會用手裡的石柱... 在獵人手遊中,初探失美樂是一個比較難打的副本,小夥伴們知道怎麼過嗎,感興趣的小夥伴就跟隨小編一起來看看吧。
  • 獵人手遊初探失美樂怎樣打?初探失美樂副本打法攻略[視頻][多圖]
    獵人手遊初探失美樂應該怎麼過?很多小夥伴在打初探失美樂副本的時候都覺得很難打,打了幾次都沒過,不知道要怎麼打才好,這個副本還是有一定難度的,下面小編為大家帶來初探失美樂副本打法攻略,希望能幫到大家!初探失美樂副本打法攻略等級達到46級後,可解鎖遺蹟副本中的第一個副本[初探失美樂]。通關初探失美樂副本,可機率掉落神器級裝備、虛空探險許可證、遺蹟探險許可證等道具。
  • 什麼是 LLVM?Swift, Rust, Clang 等語言背後的原力
    llvmpy 在 2015 年後就沒有進行維護了 —— 這對於任何軟體項目都是不利的,在使用 LLVM 時更是如此,因為每個版本的 LLVM 都有一些變化。由創建 Numba 的團隊開發的 llvmlite 已經成為當前在 Python 中的 LLVM 的競爭者。它只實現了 LLVM 功能的一個子集,正如 Numba 項目的需求所規定的那樣。
  • 通過LLVM 在 Android 上運行 Swift 代碼
    非常幸運的是已經包括了一個 LLVM 工具鏈,我們可以利用  llc (LLVM static compiler) :$NDK/toolchains/llvm-3.5/prebuilt/darwin-x86_64/bin/llc  -mtriple=armv7-none-linux-androideabi  -filetype=obj  add.ll非常棒,所以我們已經構建了一個
  • 初探虹吸洞PUMATUANA CAVE 體驗迷幻鹽躍層
    ⊙第一潛:8月25日 20:00 最大深度6米 潛水時間:70分鐘PUMATUANA CAVE的第一次初探開始了。本次初探王遠使用的是JJ CCR,韋柏使用的是開放式雙瓶氣瓶,作為初探以及對洞內深度的了解,兩人均未攜帶階段瓶,並按照韋柏的氣量計劃來作了下潛計劃。
  • 《獵人手遊》初探失美樂怎麼過 平民初探失美樂副本通關打法攻略
    導 讀 獵人手遊初探失美樂副本怎麼過,這是遊戲中一個副本,不少的玩家也是覺得這個副本的難度比較的高,玩家也是沒有什麼好的方法
  • 《獵人》手遊初探失美樂怎麼玩 初探失美樂玩法攻略
    導 讀 獵人手遊的副本初探失美樂可以為玩家帶來不少的獎勵,玩家可以從中獲得更加強力的裝備。
  • 七夕|牛郎織女傳說之初探
    這得先從牛郎織女故事本身說起,其故事淵源如何形成,其故事框架如何形成,其故事內容如何形成……一、初探故事淵源的形成中國歷來就對數字比較迷信,九五之尊是個證明。古代民間把正月正、二月二、三月三、五月五、六月六、七月七、九月九這「七重」列為吉慶日。
  • 《獵人》手遊初探失樂美攻略,掌握副本機制,裝備提升不再愁
    遊戲在46級時,會開放「遺蹟探險」功能,同步開放副本「初探失樂美」。順著任務引導,玩家自然會熟練的點擊進本開打三連來一發。但進本後會突然發現,這個引導任務比起之前的所有新手引導,難度驟然提升。很多人不明就裡的在吃藥、復活一套之後,被BOSS擊殺送出副本。而如果不清楚打法,組隊打失樂美,那簡直就跟葫蘆娃救爺爺一樣:成了盲目送人頭。
  • 初探兒童木工與語文的結合
    好了,這次初探就到這裡,希望給從業者有新的兒童木工視角幫助,感謝大家的觀看。
  • Qt 初探信號槽
    初探信號槽看過了簡單的 Hello, world! 之後,下面來看看 Qt 最引以為豪的信號槽機制!
  • C++設計模式Singleton初探
    C++設計模式Singleton初探Singleton模式的特點是保證一個類僅能創建一個實例,即程序運行中
  • 雙語:中央電視臺部分欄目名稱英譯初探
    新東方網>英語>英語學習>英語閱讀>雙語新聞>正文雙語:中央電視臺部分欄目名稱英譯初探 2004-05-11 16:13 來源:新東方論壇 作者: