什麼是LLVM-程序員必備技能

一、簡介

LLVM項目的發展起源於2000年伊利諾伊大學 維克拉姆·艾夫 與 克里斯·拉特納 的研究,他們想要為所有靜態及動態語言創造出動態的編譯技術。LLVM是以BSD許可來發展的開源軟件。2005年,蘋果計算機僱用了克里斯·拉特納及他的團隊為蘋果計算機開發應用程序系統,LLVM為現今Mac OS X及iOS開發工具的一部分。

LLVM是編譯器的基礎設施,以C++寫成,包含一系列模塊化的編譯器組件和工具鏈,用來開發編譯器前端和後端。它是為了任意一種編程語言而寫成的程序,利用虛擬技術創造出編譯時期、鏈接時期、運行時期以及“閒置時期”的最優化。它最早以C/C++為實現對象,而當前它已支持包括ActionScript、Ada、D語言、Fortran、GLSL、Haskell、Java字節碼、Objective-C、Swift、Python、Ruby、Rust、Scala以及C#等語言。

LLVM的命名最早源自於底層虛擬機(Low Level Virtual Machine)的首字母縮寫,LLVM開始成長之後,成為眾多編譯工具及低級工具技術的統稱,使得這個名字變得更不貼切,現今LLVM已單純成為一個品牌,適用於LLVM下的所有項目,包含LLVM中介碼(LLVM IR)、LLVM調試工具、LLVM C++標準庫等。

二、描述

LLVM提供了完整編譯系統的中間層,它會將中間語言(Intermediate Representation,IR)從編譯器取出與最優化,最優化後的IR接著被轉換及鏈接到目標平臺的彙編語言。LLVM可以接受來自GCC工具鏈所編譯的IR,包含它底下現存的編譯器。

LLVM也可以在編譯時期、鏈接時期,甚至是運行時期產生可重新定位的代碼。

LLVM支持與語言無關的指令集架構及類型系統。LLVM允許代碼被靜態的編譯,包含在傳統的GCC系統底下,或是類似JAVA等後期編譯才將IF編譯成機器代碼所使用的即時編譯(JIT)技術。

它的類型系統包含基本類型(整數或是浮點數)及五個複合類型(指針、數組、向量、結構及函數),在LLVM具體語言的類型建制可以以結合基本類型來表示,舉例來說,C++所使用的class可以被表示為結構、函數及函數指針的數組所組成。

LLVM JIT編譯器可以最優化在運行時期時程序所不需要的靜態分支。這個特色被使用在Mac OS X Leopard(v10.5)底下OpenGL的管線,OpenGL堆棧下的繪圖程序被編譯為IR,接著在機器上運行時被編譯,當系統擁有高端GPU時,這段程序會進行極少的修改並將傳遞指令給GPU,當系統擁有低級的GPU時,LLVM將會編譯更多的程序,使這段GPU無法運行的指令在本地端的中央處理器運行。LLVM增進了使用Intel GMA芯片等低端機器的性能。一個類似的系統發展於Gallium3D LLVMpipe,它已被合併到GNOME,使其可運行在沒有GPU的環境。

根據2011年的一項測試,GCC在運行時期的性能平均比LLVM高10%。而2013年測試顯示,LLVM可以編譯出接近GCC相同性能的運行碼。

編譯器

LLVM已經成為多個編譯器和代碼生成相關子項目的母項目。

前端

LLVM最初被用來取代現有於GCC堆棧的代碼產生器,許多GCC的前端已經可以與其運行,LLVM當前支持Ada、C語言、C++、D語言、Fortran、Haskell、Julia (編程語言)、Objective-C、Rust及Swift的編譯,它使用許多的編譯器,有些來自4.0.1及4.2的GCC。

LLVM引發一些人來為許多語言開發新的編譯器,其中一個最引發注意的就是Clang,它是一個新的編譯器,同時支持C、Objective-C以及C++。主要來自蘋果計算機的支持,Clang的目的用以取代GCC系統底下的C/Objective-C編譯器,在當代的系統,他較為容易與集成開發環境(IDE)集成,而且對於線程有更好的支持。Clang從3.8版本開始已經支持OpenMP。GCC底下Objective-C的開發已經停滯,而蘋果計算機已經將其支持移至其他的維護分支。

中間端

LLVM的核心是中間端表達式(Intermediate Representation,IR),一種類似彙編的底層語言。IR是一種強類型的精簡指令集,並對目標指令集進行了抽象。例如,目標指令集的函數調用慣例被抽象為callret指令加上明確的參數。另外,IR採用無限個數的暫存器,使用如%0,%1等形式表達。LLVM支持三種表達形式:人類可讀的彙編,在C++中對象形式和序列化後的bitcode形式。

例如,一個簡單的Hello World程序可以表達為如下的彙編形式:

@.str = internal constant [14 x i8] c"hello, world\0A\00"
declare i32 @printf(i8*, ...)
define i32 @main(i32 %argc, i8** %argv) nounwind {

entry:
%tmp1 = getelementptr [14 x i8], [14 x i8]* @.str, i32 0, i32 0
%tmp2 = call i32 (i8*, ...) @printf( i8* %tmp1 ) nounwind
ret i32 0
}

後端

至3.4版本的LLVM已經支持多種後端指令集,比如ARM、Qualcomm Hexagon、MIPS、Nvidia並行指令集(PTX;在LLVM文檔中被稱為NVPTX),PowerPC、AMD TeraScale[19]、AMD Graphics Core Next(GCN)、SPARC、z/Architecture(在LLVM文檔中被稱為SystemZ)、x86、x86-64和XCore。有部分平臺功能並沒有完全實現。但x86、x86-64、z/Architecture、ARM和PowerPC的基本所有功能都有實現。

LLVM機器碼(MC)子項目是LLVM將機器指令從文字形式轉換至機器碼的形式。在之前LLVM依靠系統或是平臺專門的工具鏈將彙編翻譯為機器碼。LLVM機器碼的集成彙編器已經支持絕大多數LLVM的目標平臺,如x86、x86-64、ARM和ARM64。對另一些平臺,如幾種MIPS平臺,彙編器支持已經加入但仍在beta階段。

鏈接器

lld鏈接器子項目旨在為LLVM開發一個內置的,平臺獨立的鏈接器,去除對所有第三方鏈接器的依賴。在2017年5月,lld已經支持ELF、PE/COFF、 和Mach-O。在lld支持不完全的情況下,用戶可以使用其他項目,如GNU ld鏈接器。 lld支持鏈接時優化。當LLVM鏈接時優化被啟用時,LLVM可以輸出bitcode而不是本機代碼,而本機代碼生成由鏈接器優化處理。

什麼是LLVM-程序員必備技能


分享到:


相關文章: