簡介
LLVM是一套提供編譯器基礎設施的開源項目,是用 C++ 編寫,包含一系列模塊化的編譯器組件和工具鏈,用來開發編譯器前端和后端。它是為了任意一種編程語言而寫成的程序,利用虛擬技術創造出編譯時期、鏈接時期、執行時期以及“閑置時期”的優化。
LLVM的命名源自於底層虛擬機(Low Level Virtual Machine)的首字母縮寫,導致不了解它的人以為它是類似於 JVM(Java Virtual Machine) 的虛擬機,實際上這個項目的范圍並不局限於創建一個虛擬機,而是包括 LLVM 中介碼(LLVM IR)、LLVM調試工具、LLVM C++ 標准庫等一系列編譯工具及低端工具技術的集合。
傳統的靜態編譯器設計是三階段設計,其主要組件是前端、優化器和后端。

前端負責詞法分析、語法分析、語義分析、生成中間代碼等功能。
優化器負責進行各種轉換以嘗試提高代碼的運行時間,例如消除冗余計算,並且通常或多或少獨立於語言和目標。
后端(也稱為代碼生成器)負責將代碼映射到目標指令集。除了編寫正確的代碼外,它還負責生成利用所支持架構的不尋常特性的良好代碼。編譯器后端的常見部分包括指令選擇、寄存器分配和指令調度。
該模型同樣適用於解釋器和 JIT 編譯器。JVM 也是該模型的一個實現,它使用 Java 字節碼作為前端和優化器之間的接口。
而 LLVM 被設計為支持多種源語言或目標架構,它提供了一套適合編譯器系統的中間語言,如果編譯器在其優化器中使用這個中間語言表示,則可以為任何可以編譯到它的語言編寫前端,並且可以為任何可以從它編譯的目標編寫后端。

使用這種設計,移植編譯器以支持新的源語言只需要實現新的前端,即可以重用現有的優化器和后端;同樣想增加支持新的目標架構也只需要實現新的后端。而如果按傳統設計,前端和后端實際是耦合在一起,實現新的源語言或支持新的目標架構將需要從頭開始,要支持 N 目標和 M 源語言將需要 N*M 個編譯器。
LLVM IR
LLVM提供了一套適合編譯器系統的中間語言(Intermediate Representation,IR),有大量變換和優化都圍繞其實現,經過變換和優化后的中間語言,可以轉換為目標平台相關的匯編語言代碼。
該中間語言與具體的語言、指令集、類型系統無關,其中每條指令都是靜態單賦值形式(SSA), 即每個變量只能被賦值一次。這有助於簡化變量之間的依賴分析。
以下是簡單的 LLVM IR 代碼:
define i32 @add1(i32 %a, i32 %b) {
entry:
%tmp1 = add i32 %a, %b
ret i32 %tmp1
}
define i32 @add2(i32 %a, i32 %b) {
entry:
%tmp1 = icmp eq i32 %a, 0
br i1 %tmp1, label %done, label %recurse
recurse:
%tmp2 = sub i32 %a, 1
%tmp3 = add i32 %b, 1
%tmp4 = call i32 @add2(i32 %tmp2, i32 %tmp3)
ret i32 %tmp4
done:
ret i32 %b
}
上述代碼對應的 C 語言代碼為:
unsigned add1(unsigned a, unsigned b) {
return a+b;
}
unsigned add2(unsigned a, unsigned b) {
if (a == 0) return b;
return add2(a-1, b+1);
}
從這個例子可以看出,LLVM IR 是一種強類型的精簡指令集( RISC )。像真正的 RISC 指令集一樣,它支持簡單指令的線性序列,如加法、減法、比較和分支。這些指令采用三地址形式,這意味着它們接受一定數量的輸入並在不同的寄存器中產生結果。LLVM IR 支持標簽,通常看起來像一種奇怪的匯編語言形式。
與大多數 RISC 指令集不同,LLVM 使用簡單的類型系統進行強類型化(例如,i32 是一個 32 位整數,i32** 是一個指向 32 位整數的指針),並且機器的一些細節被抽象掉了。例如,調用約定是通過指令和顯式參數 call 抽象出來的。ret 與機器代碼的另一個顯着區別是 LLVM IR 不使用一組固定的命名寄存器,它使用一組無限的以 % 字符命名的臨時寄存器。
LLVM IR 支持三種表達形式:人類可讀的匯編、在C++中對象形式、序列化后的 bitcode 形式。
編譯
LLVM允許代碼被靜態的編譯,包含在傳統的GCC系統底下,者通過實時編譯(JIT)機制將中間表示轉換為機器碼(類似 Java)。
LLVM 類型系統包含基本類型(整數或是浮點數)及五個復合類型(指針、數組、向量、結構及函數),在LLVM具體語言的類型建制可以以結合基本類型來表示,舉例來說,C++所使用的class可以被表示為結構、函數及函數指針的數組所組成。
LLVM 提供了 Clang 作為官方的編譯器前端,同時支持 C、C++、Objective-C 和 Objective-C++ 語言。主要來自 Apple 公司的贊助支持,Clang 的目的用以取代 GCC 系統底下的 C / Objective-C 編譯器,在當代的系統,它較為容易與集成開發環境(IDE)集成,而且對於線程有更好的支持。許多 GCC 的前端也已經可以與其運行,LLVM目前支持 Ada、C語言、C++、D語言、Fortran、Haskell、Julia、Objective-C、Rust 及 Swift 等語言的編譯。
