認識 LLVM


簡介

LLVM是一套提供編譯器基礎設施的開源項目,是用 C++ 編寫,包含一系列模塊化的編譯器組件和工具鏈,用來開發編譯器前端和后端。它是為了任意一種編程語言而寫成的程序,利用虛擬技術創造出編譯時期、鏈接時期、執行時期以及“閑置時期”的優化。

LLVM的命名源自於底層虛擬機(Low Level Virtual Machine)的首字母縮寫,導致不了解它的人以為它是類似於 JVM(Java Virtual Machine) 的虛擬機,實際上這個項目的范圍並不局限於創建一個虛擬機,而是包括 LLVM 中介碼(LLVM IR)、LLVM調試工具、LLVM C++ 標准庫等一系列編譯工具及低端工具技術的集合。

傳統的靜態編譯器設計是三階段設計,其主要組件是前端、優化器和后端。

傳統的靜態編譯器設計

前端負責詞法分析、語法分析、語義分析、生成中間代碼等功能。
優化器負責進行各種轉換以嘗試提高代碼的運行時間,例如消除冗余計算,並且通常或多或少獨立於語言和目標。
后端(也稱為代碼生成器)負責將代碼映射到目標指令集。除了編寫正確的代碼外,它還負責生成利用所支持架構的不尋常特性的良好代碼。編譯器后端的常見部分包括指令選擇、寄存器分配和指令調度。

該模型同樣適用於解釋器和 JIT 編譯器。JVM 也是該模型的一個實現,它使用 Java 字節碼作為前端和優化器之間的接口。

而 LLVM 被設計為支持多種源語言或目標架構,它提供了一套適合編譯器系統的中間語言,如果編譯器在其優化器中使用這個中間語言表示,則可以為任何可以編譯到它的語言編寫前端,並且可以為任何可以從它編譯的目標編寫后端。

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 等語言的編譯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM