[轉帖]什麼是 LLVM?Swift, Rust, Clang 等語言背後的支持

要了解用於以編程方式生成機器原生代碼的編譯器框架是如何讓新語言的推出以及對現有的語言進行加強比以往更加容易了。html

https://www.oschina.net/translate/what-is-llvm-the-power-behind-swift-rust-clang-and-more?print

debian 好像也在進行 RISC-V 的遷移

可是由於LLVM暫時不知此後 不少包 沒遷移完. 

感受東西好多的說.

 

新的語言,還有對現有語言的提高,在整個編程環境中正大行其道。Mozilla 的 RustApple 的 SwiftJetbrains 的 Kotlin,以及許多其它的語言都給開發者在速度、安全性、便利性、可移植性還有能力這些方面提供了新的選擇。java

爲何如今正當時呢?一個大因素就是那些用來構建語言的新工具,特別是編譯器。它們中首當其衝就是 LLVM (底層虛擬機 Low-Level Virtual Machine),這是一個開源項目,最開始做爲伊利諾伊州大學的一個研究項目由 Swift 語言的創始人 Chris Lattner 進行開發。node

LLVM 使建立新語言變得更加容易,同時也能夠加強現有語言的開發。它提供了一些工具,用於自動執行語言建立任務中最不討人喜歡的部分:建立一個編譯器,將輸出的代碼移植到多個平臺和架構,編寫代碼來處理常見的語言隱喻,好比異常。它的自由受權意味着它能夠自由地做爲軟件組件重用或做爲服務部署。python

使用 LLVM 的語言名冊中有許多熟悉的名字。蘋果的 Swift 語言使用 LLVM 做爲它的編譯器框架,而 Rust 則將 LLVM 做爲其工具鏈的核心組件。並且,許多編譯器都有一個 LLVM 版本,如 Clang、C/C++ 編譯器(這個名稱叫作「C-lang」),它自己就是一個與 LLVM 緊密相連的項目。而 Kotlin,名義上是一種 JVM 語言,正在開發一種名爲 Kotlin Native 的語言版本,它使用 LLVM 來編譯成機器原生代碼。git

LLVM 定義

在它的核心,LLVM 是一個以編程方式建立機器原生代碼的庫。開發人員使用該 API 以一種稱爲中間代理或 IR 的格式生成指令。而後 LLVM 能夠將 IR 編譯成一個獨立的二進制文件,或者在另外一個程序(如語言解釋器)的上下文中執行 JIT (just-in-time) 編譯。github

LLVM 的 API 爲開發在編程語言中發現的許多常見結構和模式提供了原始的方式。例如,幾乎每種語言都有函數和全局變量的概念。LLVM 將函數和全局變量做爲其 IR 中的標準元素,所以,你只需在乎 LLVM 的實現,並關注須要注意的語言部分,而不是花費時間和精力從新建立這些特定的輪子。npm

這是一個 LLVM 中間代理(IR)的例子。右邊是一個簡單的 C 程序;左邊是由 Clang 編譯器翻譯成 LLVM IR 的代碼。編程

LLVM:專爲可移植性而生

關於 LLVM 的一個說法是它像常提到的 C 編程語言:C 語言有時候被認爲是一種便攜式、高級的彙編語言,由於它能夠緊密地映射到系統硬件的結構,並且它已經被移植到幾乎全部的系統架構。可是,C 語言只是做爲一種可移植的彙編語言,是其工做方式的另外一種效果;這並非它的設計目標之一。swift

相比之下,LLVM 的 IR 是從一開始就設計爲可移植的組件。它實現這種可移植性的一種方法是提供獨立於任何特定機器架構的原語。例如,整數類型不侷限於底層硬件的最大位寬度(例如 32 或 64 位),您能夠根據須要使用盡量多的比特字節來建立基本的整數類型,好比 128 位整數。您也沒必要擔憂手工輸出來匹配特定處理器的指令集;LLVM 也會爲你處理這個問題。後端

若是你但願看到 LLVM IR 的現場示例,請訪問 ELLCC 項目網站,並嘗試在瀏覽器中將 C 代碼轉換爲 LLVM IR 的現場演示 Demo

編程語言中如何使用 LLVM

LLVM 最多見的用例是做爲一種語言的預先(AOT ahead-of-time)編譯器。但 LLVM 也能夠用於即時編譯。

用 LLVM 進行即時編譯

有些狀況下須要在運行時動態生成代碼,而不是預先編譯。例如,Julia 語言就是使用 JIT 編譯代碼,由於它須要快速運行,並經過 REPL(read-eval-print loop)或交互式提示與用戶交互。.Net 和 Mono 能夠選擇經過 LLVM 後端方式編譯爲原生代碼

Numba 是一個 Python 的數學加速包,JIT 將所選擇的 Python 函數編譯成機器碼。它也能夠預先編譯使用 Numba 裝飾器裝飾的代碼,可是(好比 Julia)Python 做爲一種快速發展的解釋性語言,使用 JIT 編譯來產生這樣的代碼更好地補充了 Python 的交互式工做流,比 Python 的預先編譯方式更好。

其餘人正在嘗試以非正統方式使用 LLVM 做爲 JIT 編譯方式,例如編譯 PostgreSQL 查詢,聽說性能提升了五倍。


Numba 使用 LLVM 進行即時編譯數字代碼並加速其執行。JIT 加速過的 sum2d 函數的執行速度比常規 Python 代碼快 139 倍。

使用 LLVM 進行自動代碼優化

LLVM 不只將 IR 編譯爲原生機器碼。你也能夠直接以編程的方式在整個連接過程當中高度精細地優化代碼。優化方式是至關積極主動的,可以實現包括內聯函數在內,消除死代碼(包括未使用的類型聲明和函數參數)和展開循環這些事情。

這裏再一次強調,LLVM 的力量讓你沒必要本身實現全部這一切。LLVM 能夠爲您處理它們,您也能夠根據須要直接禁用。例如,若是你想要一些更小的二進制代碼,那麼你可讓你的編譯器告訴 LLVM 禁用循環展開。

使用 LLVM 的領域特定語言

LLVM 已被用於生成多種通用語言的編譯器,但它也可用於生成高度垂直或排他性問題域的語言。從某種意義上說,這就是 LLVM 最閃光的地方,由於它在創造這樣一類語言方面消除了諸多苦差事,並使其表現良好。

例如,Emscripten 項目採用 LLVM IR 代碼並將其轉換爲 JavaScript,理論上支持使用 LLVM 做爲後端的任何語言導出可在瀏覽器中運行的代碼。長期規劃是支持基於 LLVM 的後端並可以生成 WebAssembly 代碼,Emscripten 是 LLVM 靈活性的一個很好的例子。

LLVM 能夠被使用的另外一種方法是將特定領域的擴展添加到現有語言。Nvidia 使用 LLVM 建立了 Nvidia CUDA 編譯器,該編譯器容許語言爲 CUDA 添加原生支持,它是做爲你生成的原生代碼的一部分編譯的,而不是經過附帶的庫進行調用的。

在不一樣語言中使用 LLVM

使用 LLVM 的典型方式是經過你所熟悉的語言來編寫代碼(固然也要有支持 LLVM 的庫)。

兩種常見的可選語言是 C 和 C++。許多 LLVM 開發者會由於如下的緣由而默認選擇其中的一個:

  • LLVM 自己是用 C++ 編寫的

  • LLVM 的 API 以 C 和 C++ 版本提供

  • 大量的語言開發每每會以 C/C++ 做爲一個基礎

不過,這兩種語言並非惟一的選擇。許多語言均可以原生調用 C 語言庫,因此理論上能夠用任何這樣的語言進行 LLVM 開發。但須要有一個實際的語言庫能夠很好地封裝 LLVM API。幸運的是,許多語言和語言運行時都有這樣的庫,包括 C#/.Net/MonoRustHaskellOCAMLNode.jsGo, 和 Python

須要注意的是,一些與 LLVM 的語言綁定可能不完整。以 Python 爲例,有不少種綁定選擇,但每一個選項的完整性和實用性各不相同:

  • LLVM 項目維護着本身的一套到 LLVM 的 C API 的綁定,可是目前他們沒有繼續維護。

  • llvmpy 在 2015 年後就沒有進行維護了 —— 這對於任何軟件項目都是不利的,在使用 LLVM 時更是如此,由於每一個版本的 LLVM 都有一些變化。

  • 由建立 Numba 的團隊開發的 llvmlite 已經成爲當前在 Python 中的 LLVM 的競爭者。它只實現了 LLVM 功能的一個子集,正如 Numba 項目的需求所規定的那樣。可是這個子集知足了絕大多數 LLVM 用戶所需。

  • llvmcpy 旨在爲 C 庫帶來最新的 Python 綁定,它以自動化的方式保持更新,並使用 Python 的習慣用法來訪問它們。llvmcpy 還處於早期階段,可是已經能夠用 LLVM API 作一些基本的工做。

若是你對如何使用 LLVM 庫構建語言感興趣,不妨看看 LLVM 的建立者撰寫的使用 C++ 或 OCAML 語言的教程,它將一步步指導你建立一種簡單的名爲 Kaleidoscope 的語言。它還被移植到其餘語言之上:

  • Haskell:參考原始教程的直接移植。

  • Python在此網站的教程和原始版本很是相近,而另外一個版本則是用交互式命令行進行更爲雄心勃勃的重寫。這兩種版本都使用 llvmlite 做爲到 LLVM 的綁定。

  • Rust 和 Swift:不可避免地,咱們不得不將該教程移植到這兩種語言之上,它們都是由 LLVM 自身幫助使其誕生的。

最後,這個教程也有其餘國家語言版本的。這裏有中文版,分別是使用原始的 C++ 和 Python 版本。

LLVM 還沒有實現的功能

瞭解 LLVM 能夠實現的功能的同時,有必要知道 LLVM 目前還沒有實現的功能。

例如,LLVM 不解析語言的語法。由於目前已經有許多工具實現這個功能,好比 lex/yaccflex/bison,以及ANTLR。解析語法就意味着必須從編譯中解耦出來,難怪 LLVM 並無涉及這個領域。

LLVM 也不會直接干涉到開發語言的軟件文化,好比安裝編譯器的二進制文件、如何在安裝中管理軟件包、升級工具鏈 —— 這些都須要開發者本身去實現。

最後也是最重要的一點是,LLVM尚未對部分通用語言成分給出原語。許多語言都具備某種垃圾回收的內存管理方式,或者是做爲管理內存的主要方式,或者做爲對 RAII ( C++ 底層實現的自動垃圾回收,表面使用 Rust 語法)等策略的附屬方式。LLVM 並不會給你一個垃圾回收機制,可是它提供了實現垃圾回收的工具,它容許在代碼中使用元數據標記,讓編寫垃圾回收器變得更加容易。

儘管如此,可是 LLVM 將來還有有可能添加原生的機制來實現垃圾回收機制。LLVM 正在快速發展中,大概 6 個月就會有一次大版本的更新。因爲當前的許多語言都使用 LLVM 做爲開發的核心,所以 LLVM 的迭代速度只會更快而不會放慢。

相關文章
相關標籤/搜索