LLVM教學(1): 5分鐘了解LLVM架構

LLVM是用來設計Compiler很重要的工具。在量子計算的程式設計中,很大部份的軟體研究都是著重於量子計算程式的優化 (Quantum Circuit Optimization)。因此Quantum Compiler的設計也很重要,而使用LLVM來設計Quantum Compiler是很常見的。因此這篇文章LLVM教學將會介紹LLVM的基本架構。

這邊文章主要是根據LLVM的這則介紹影片所改寫而成。有興趣的人歡迎去看原始影片。

什麼是LLVM?

LLVM是一個用來產生編譯器的Open Source Project (https://github.com/llvm/llvm-project)。例如C/C++的Compiler Clang就是一個LLVM所產生的compiler。

在LLVM open source tree中可以看到llvm-project就是此project的根目錄。llvm-project/clang就是作為C/C++/CUDA等程式語言的front-end。llvm-project/llvm就是LLVM的主要核心目錄,包含了middle-end以及backends。

如何Configure和Build LLVM?

Configure LLVM:

只需要使用cmake command即可configure所要編譯的目標項目。例如下面,我們要產生clang以及lld (一個LLVM tool),則我們只需要在LLVM_ENABLE_PROJECTS指定好即可。

cmake /dir/llvm-project/llvm -DLLVM_ENABLE_PROJECTS='clang;lld'

Build LLVM:

make -j

其他在Configure時常用的指令包括 (used with cmake command):

CMAKE_BUILD_TYPE={Release, Debug}
LLVM_CCACHE_BUILD={ON, OFF}

Compilation流程

以下是一個C++程式碼被編譯成為機器碼的流程。一開始利用clang進行frontend處理產生LLVM IR (*.ll)檔案,接著可以使用opt進行middle-end的處理,執行不同的compilation pass去優化程式。接著利用llc產生進行backend處理產生最後的機器碼。

LLVM compilation flow
Compilation Flow

從C到LLVM IR

使用clang以及以下指令。

-S:是產生人能夠讀的形式。

-O3 -disable-llvm-passes:讓compiler準備O3優化但不要執行。

-emit-llvm:讓compiler產生出LLVM IR (*.ll)。

-mem2reg:把memory定址轉換成register定址方便閱讀。

-instnamer:如果instruction沒有名字就給它一個名字方便閱讀。

$ clang -O3 -Xclang -disable-llvm-passes -S -emit-llvm input.c -o input.ll
$ opt -S -mem2reg -instnamer input.ll -o before_opt.ll

LLVM IR內容

這邊顯示一個簡單的C範例

int foo(int a, int b){
    return a + b;
}

以下是.ll檔案沒有使用-O3的狀況。target datalayout是backend的格式。而看到i32表示是一個32bits的data type,在原始範例中為int type。這裡可以看到這個Function一開始先產生了%a.addr以及%b.addr的空間,個別為32bits。然後把%a和%b的值分別用store存入%a.addr和%b.addr的位置,接著用load指令將%a.addr和%b.addr的位置的值讀取到register %0與%1中,最後把%0和%1進行相加存到%add,然後return %add為一個i32 type。在LLVM的世界中,可使用的暫存器有無限多個,所以可以隨意使用%0, %1, …%n來產生暫存器。

如果使用-O3來產生LLVM IR,可以看到store與load指令都被直接省略了,因為這個動作是可以被最佳化而不用執行的。現在產生的LLVM IR是直接將%a與%b的值拿來加在一起就存入%add中然後return i32 %add了。

LLVM-IR 結構

LLVM class結構上從llvm::Module開始為最上層,在一個Module裡面會有llvm::GlobalVariable以及llvm::Function。在llvm:Function裡面會有llvm:BasicBlock。llvm::BasicBlock裡面會有llvm::Instruction。而在llvm::Instruction內會有llvm::ICmpInst與llvm::BranchInst。大致如下圖所示。

LLVM-IR是靜態單賦值形式 (Static single-assignment form,SSA-form),也就是每個變數只會被賦值一次,且假設有無窮多可使用的暫存器(Register)。在架構上,一個Module為一連串的GlobalVariables and Functions,一個Function為一連串的Basic Blocks,一個Basic Block為一連串的指令。一個指令就是一系列的運算子與運算元(Operators and operands)。

LLVM-IR的符號

  • Global symbols會使用”@”作為變數名稱開頭
  • Local symbols會使用”%”作為變數名稱開頭
  • Basic block名稱當用到時會使用”%”開頭
  • Basic block名稱會使用”:”做為結尾

X. Ryan
X. Ryan

Hello!我是一個在矽谷工作,有軟體工程背景的量子計算科學家。這裡分享的內容主要是把平常研究開發時所用的小工具以及看過的東西記錄下來,同時也分享一些日常生活瑣事。

文章: 49