LLVM的今生

LLVM簡介:

LLVM是構架編譯器(compiler)的框架系統,以C++編寫而成,用於優化以任意程序語言編寫的程序的編譯時間(compile-time)、鏈接時間(link-time)、運行時間(run-time)以及空閒時間(idle-time),對開發者保持開放,併兼容已有腳本。

LLVM最早的時候是Illinois的一個研究項目,主要負責人是Chris Lattner,他現在就職於Apple. Apple 目前也是llvm項目的主要贊助者之一。

在理解LLVM時,我們可以認為它包括了一個狹義的LLVM和一個廣義的LLVM。廣義的LLVM其實就是指整個LLVM編譯器架構,包括了前端、後端、優化器、眾多的庫函數以及很多的模塊;而狹義的LLVM其實就是聚焦於編譯器後端功能(代碼生成、代碼優化、JIT等)的一系列模塊和庫。

LLVM優勢:

傳統編譯器分三個階段:

前端(Frontend)-- 優化器(Optimizer)-- 後端(Backend)

前端負責分析源代碼,可以檢查語法級錯誤,並構建針對語言的抽象語法樹(AST);抽象語法樹可以進一步轉換為優化,最終轉為新的表示方式,然後再交給讓優化器和後端處理;

最終由後端生成可執行的機器碼。

llvm也分三個階段,但是設計上略微的有些區別, LLVM不同的就是對於不同的語言它都提供了同一種中間表示:

前端可以使用不同的編譯工具對代碼文件做詞法分析以形成抽象語法樹AST,然後將分析好的代碼轉換成LLVM的中間表示IR(intermediate representation);中間部分的優化器只對中間表示IR操作,通過一系列的pass對IR做優化;後端負責將優化好的IR解釋成對應平臺的機器碼。LLVM的優點在於,中間表示IR代碼編寫良好,而且不同的前端語言最終都轉換成同一種的IR。

為什麼使用三段式設計?優勢在哪裡?首先解決一個很大的問題:假如有N種語言(C、OC、C++、Swift...)的前端,同時也有M個架構(模擬器、arm64、x86...)的target,是否就需要N*M個編譯器?三段式架構的價值就提現出來了,通過共享優化器的中轉,很好的解決了這個問題。

Clang和LLVM關係:

Clang是一個C++編寫、基於LLVM、發佈於LLVM BSD許可證下的C/C++/Objective-C/Objective-C++編譯器。那麼為什麼已經有了GCC還要開發Clang呢?Clang相比於GCC有什麼優勢呢? 其實,這也是Clang當初在設計開發的時候所主要考慮的原因。Clang是一個高度模塊化開發的輕量級編譯器,它的編譯速度快、佔用內存小、非常方便進行二次開發。

LVM和Clang的關係是怎樣的呢。我們將它們對應於傳統的編譯器當中的幾個獨立的部分,這樣能夠更加方便明確生動的表述。

其實,對應到這個圖中,我們就可以非常明確的找出它們的對應關係。LLVM與Clang是C/C++編譯器套件。對於整個LLVM的框架來說,包含了Clang,因為Clang是LLVM的框架的一部分,是它的一個C/C++的前端。Clang使用了LLVM中的一些功能,目前知道的就是針對中間格式代碼的優化,或許還有一部分生成代碼的功能。從源代碼角度來講,clang是基於LLVM的一個工具。而功能的角度來說,LLVM可以認為是一個編譯器的後端,而clang是一個編譯器的前端,他們的關係更加的明瞭,一個編譯器前端想要程序最終變成可執行文件,是缺少不了對編譯器後端的介紹的。

LLVM編譯工具鏈編譯流程:

使用LLVM的對一門語言編譯的簡圖如下所示:

LLVM編譯一個源文件的過程:預處理 -> 詞法分析 -> Token -> 語法分析 -> AST -> 代碼生成 -> LLVM IR -> 優化 -> 生成彙編代碼 -> Link -> 目標文件

完全需要我們手工,或者依靠其他工具如lex, yacc來做的事情,是從源代碼到token的詞法分析和從token到AST的語法分析;詞法分析的輸出是將源代碼解析成一個個的token。這些token就是有類型和值的一些小單元,比如是關鍵字,還是數字,還是標識符,從AST轉LLVM開始,LLVM就開始提供一系列的工具幫助我們快速開發。從IR(中間指令代碼)到DAG(有向無環圖)再到機器指令,針對常用的平臺,LLVM有完善的後端。也就是說,我們只要完成了到IR這一步,後面的工作我們就享有和Clang一樣的先進生產力了。

CodeGen負責將語法樹從頂至下遍歷,翻譯成LLVM IR,LLVM IR是Frontend的輸出,也是LLVM Backerend的輸入,橋接前後端。

LLVM命令:

可以使用 llc 將 LLVM 字節代碼轉換成特定於平臺的彙編代碼

lli 可以通過解釋器或使用高級選項中的即時 (JIT) 編譯器執行此工作

llvm-gcc 是 GNU Compiler Collection (gcc) 的修改版本,可以在使用 -S -emit-llvm 選項運行時會生成 LLVM 字節代碼。

編譯指令:

clang -c -emit-llvm test1.c -o test1.bc 編譯產生字節碼

clang -S -emit-llvm test.c -o test.ll 編譯產生可視化字節碼

llvm-dis test1.bc test1.ll bc字節碼轉為可視化字節碼ll

llvm-as test1.ll test1.bc 可視化字節碼轉為字節碼bc


分享到:


相關文章: